cpp
Advanced OOP
06_Advanced_OOP⚙️cpp
/**
* ============================================================
* C++ ABSTRACT CLASSES & PURE VIRTUAL FUNCTIONS
* ============================================================
*
* This file covers:
* - Abstract classes definition
* - Pure virtual functions
* - Interface vs abstract class
* - Abstract class hierarchies
* - Factory pattern with abstract classes
*
* Compile: g++ -std=c++17 -Wall 01_abstract_classes.cpp -o abstract
* Run: ./abstract
*
* ============================================================
*/
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <cmath>
using namespace std;
// ============================================================
// PART 1: BASIC ABSTRACT CLASS
// ============================================================
// Abstract class - has at least one pure virtual function
class Shape {
protected:
string name;
string color;
public:
Shape(const string& n, const string& c = "black")
: name(n), color(c) {
cout << " Shape constructor: " << name << endl;
}
virtual ~Shape() {
cout << " Shape destructor: " << name << endl;
}
// Pure virtual functions - MUST be implemented by derived classes
virtual double area() const = 0;
virtual double perimeter() const = 0;
virtual void draw() const = 0;
// Regular virtual function - CAN be overridden
virtual void describe() const {
cout << "A " << color << " " << name << endl;
}
// Non-virtual function
string getName() const { return name; }
string getColor() const { return color; }
void setColor(const string& c) { color = c; }
};
// Concrete class - implements all pure virtual functions
class Circle : public Shape {
private:
double radius;
public:
Circle(double r, const string& c = "red")
: Shape("Circle", c), radius(r) {
cout << " Circle constructor, radius=" << radius << endl;
}
~Circle() {
cout << " Circle destructor" << endl;
}
// Implementing pure virtual functions
double area() const override {
return M_PI * radius * radius;
}
double perimeter() const override {
return 2 * M_PI * radius;
}
void draw() const override {
cout << "Drawing " << color << " circle with radius " << radius << endl;
}
// Additional methods specific to Circle
double getRadius() const { return radius; }
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h, const string& c = "blue")
: Shape("Rectangle", c), width(w), height(h) {
cout << " Rectangle constructor, " << width << "x" << height << endl;
}
~Rectangle() {
cout << " Rectangle destructor" << endl;
}
double area() const override {
return width * height;
}
double perimeter() const override {
return 2 * (width + height);
}
void draw() const override {
cout << "Drawing " << color << " rectangle " << width << "x" << height << endl;
}
};
// ============================================================
// PART 2: ABSTRACT CLASS HIERARCHY
// ============================================================
// Abstract base
class Employee {
protected:
string name;
int id;
public:
Employee(const string& n, int i) : name(n), id(i) {}
virtual ~Employee() = default;
// Pure virtual - different employees calculate salary differently
virtual double calculateSalary() const = 0;
virtual string getRole() const = 0;
string getName() const { return name; }
int getId() const { return id; }
virtual void displayInfo() const {
cout << "ID: " << id << ", Name: " << name
<< ", Role: " << getRole()
<< ", Salary: $" << calculateSalary() << endl;
}
};
// Still abstract - doesn't implement all pure virtuals
class HourlyEmployee : public Employee {
protected:
double hourlyRate;
int hoursWorked;
public:
HourlyEmployee(const string& n, int i, double rate, int hours)
: Employee(n, i), hourlyRate(rate), hoursWorked(hours) {}
double calculateSalary() const override {
return hourlyRate * hoursWorked;
}
// getRole() still pure virtual - makes this class abstract
};
// Concrete classes
class Developer : public HourlyEmployee {
public:
Developer(const string& n, int i, double rate, int hours)
: HourlyEmployee(n, i, rate, hours) {}
string getRole() const override {
return "Developer";
}
};
class Designer : public HourlyEmployee {
public:
Designer(const string& n, int i, double rate, int hours)
: HourlyEmployee(n, i, rate, hours) {}
string getRole() const override {
return "Designer";
}
};
class Manager : public Employee {
private:
double baseSalary;
double bonus;
public:
Manager(const string& n, int i, double salary, double b)
: Employee(n, i), baseSalary(salary), bonus(b) {}
double calculateSalary() const override {
return baseSalary + bonus;
}
string getRole() const override {
return "Manager";
}
};
// ============================================================
// PART 3: PURE INTERFACE (ALL PURE VIRTUAL)
// ============================================================
// Pure interface - no data members, all pure virtual
class Drawable {
public:
virtual ~Drawable() = default;
virtual void draw() const = 0;
virtual void erase() const = 0;
virtual void moveTo(int x, int y) = 0;
};
class Clickable {
public:
virtual ~Clickable() = default;
virtual void onClick() = 0;
virtual void onDoubleClick() = 0;
virtual bool isClickable() const = 0;
};
class Resizable {
public:
virtual ~Resizable() = default;
virtual void resize(double factor) = 0;
virtual void setSize(int w, int h) = 0;
};
// Class implementing multiple interfaces
class Button : public Drawable, public Clickable, public Resizable {
private:
string label;
int x, y;
int width, height;
bool enabled;
public:
Button(const string& lbl, int x, int y, int w, int h)
: label(lbl), x(x), y(y), width(w), height(h), enabled(true) {}
// Drawable interface
void draw() const override {
cout << "Drawing button '" << label << "' at (" << x << "," << y
<< ") size " << width << "x" << height << endl;
}
void erase() const override {
cout << "Erasing button '" << label << "'" << endl;
}
void moveTo(int newX, int newY) override {
x = newX;
y = newY;
cout << "Moved button to (" << x << "," << y << ")" << endl;
}
// Clickable interface
void onClick() override {
if (enabled) {
cout << "Button '" << label << "' clicked!" << endl;
}
}
void onDoubleClick() override {
if (enabled) {
cout << "Button '" << label << "' double-clicked!" << endl;
}
}
bool isClickable() const override {
return enabled;
}
// Resizable interface
void resize(double factor) override {
width = static_cast<int>(width * factor);
height = static_cast<int>(height * factor);
cout << "Resized to " << width << "x" << height << endl;
}
void setSize(int w, int h) override {
width = w;
height = h;
}
// Button-specific methods
void setEnabled(bool e) { enabled = e; }
string getLabel() const { return label; }
};
// ============================================================
// PART 4: FACTORY PATTERN WITH ABSTRACT CLASSES
// ============================================================
// Abstract product
class Document {
public:
virtual ~Document() = default;
virtual void open() = 0;
virtual void save() = 0;
virtual void close() = 0;
virtual string getType() const = 0;
};
class TextDocument : public Document {
private:
string content;
public:
void open() override { cout << "Opening text document..." << endl; }
void save() override { cout << "Saving as .txt file..." << endl; }
void close() override { cout << "Closing text document..." << endl; }
string getType() const override { return "Text"; }
};
class PDFDocument : public Document {
public:
void open() override { cout << "Opening PDF document..." << endl; }
void save() override { cout << "Saving as .pdf file..." << endl; }
void close() override { cout << "Closing PDF document..." << endl; }
string getType() const override { return "PDF"; }
};
class SpreadsheetDocument : public Document {
public:
void open() override { cout << "Opening spreadsheet..." << endl; }
void save() override { cout << "Saving as .xlsx file..." << endl; }
void close() override { cout << "Closing spreadsheet..." << endl; }
string getType() const override { return "Spreadsheet"; }
};
// Factory class
class DocumentFactory {
public:
static unique_ptr<Document> createDocument(const string& type) {
if (type == "text") return make_unique<TextDocument>();
if (type == "pdf") return make_unique<PDFDocument>();
if (type == "spreadsheet") return make_unique<SpreadsheetDocument>();
return nullptr;
}
};
// ============================================================
// MAIN FUNCTION
// ============================================================
int main() {
cout << "============================================" << endl;
cout << " C++ ABSTRACT CLASSES" << endl;
cout << "============================================" << endl << endl;
// ========================================================
// DEMO 1: Basic Abstract Class
// ========================================================
cout << "--- DEMO 1: BASIC ABSTRACT CLASS ---" << endl << endl;
// Shape shape("Test", "red"); // ERROR: cannot instantiate abstract class
cout << "Creating shapes:" << endl;
Circle circle(5.0, "red");
Rectangle rect(4.0, 3.0, "blue");
cout << "\nUsing shapes through base pointer:" << endl;
vector<Shape*> shapes = {&circle, &rect};
for (const Shape* s : shapes) {
s->describe();
s->draw();
cout << " Area: " << s->area() << endl;
cout << " Perimeter: " << s->perimeter() << endl;
cout << endl;
}
cout << endl;
// ========================================================
// DEMO 2: Abstract Class Hierarchy
// ========================================================
cout << "--- DEMO 2: ABSTRACT HIERARCHY ---" << endl << endl;
// HourlyEmployee emp("Test", 1, 50, 40); // ERROR: still abstract
vector<unique_ptr<Employee>> employees;
employees.push_back(make_unique<Developer>("Alice", 101, 75.0, 160));
employees.push_back(make_unique<Designer>("Bob", 102, 65.0, 150));
employees.push_back(make_unique<Manager>("Charlie", 103, 8000.0, 2000.0));
cout << "Employee list:" << endl;
double totalPayroll = 0;
for (const auto& emp : employees) {
emp->displayInfo();
totalPayroll += emp->calculateSalary();
}
cout << "\nTotal payroll: $" << totalPayroll << endl;
cout << endl;
// ========================================================
// DEMO 3: Multiple Interfaces
// ========================================================
cout << "--- DEMO 3: MULTIPLE INTERFACES ---" << endl << endl;
Button okButton("OK", 100, 200, 80, 30);
// Use as Drawable
Drawable* drawable = &okButton;
drawable->draw();
drawable->moveTo(150, 250);
// Use as Clickable
Clickable* clickable = &okButton;
clickable->onClick();
clickable->onDoubleClick();
// Use as Resizable
Resizable* resizable = &okButton;
resizable->resize(1.5);
cout << endl;
// ========================================================
// DEMO 4: Factory Pattern
// ========================================================
cout << "--- DEMO 4: FACTORY PATTERN ---" << endl << endl;
vector<string> docTypes = {"text", "pdf", "spreadsheet"};
for (const auto& type : docTypes) {
auto doc = DocumentFactory::createDocument(type);
if (doc) {
cout << "Created " << doc->getType() << " document:" << endl;
doc->open();
doc->save();
doc->close();
cout << endl;
}
}
cout << endl;
// ========================================================
// ABSTRACT CLASS CONCEPTS
// ========================================================
cout << "--- ABSTRACT CLASS CONCEPTS ---" << endl << endl;
cout << "Abstract Class:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "• Has at least one pure virtual function" << endl;
cout << "• Cannot be instantiated directly" << endl;
cout << "• Can have data members" << endl;
cout << "• Can have non-virtual functions" << endl;
cout << "• Can have constructor/destructor" << endl;
cout << "\nPure Virtual Function:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "• Declared with = 0" << endl;
cout << "• virtual void func() = 0;" << endl;
cout << "• Must be overridden in derived class" << endl;
cout << "• Makes the class abstract" << endl;
cout << "\nInterface (C++ convention):" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "• All functions are pure virtual" << endl;
cout << "• No data members (except maybe static)" << endl;
cout << "• Only virtual destructor" << endl;
cout << "• Defines a contract for classes" << endl;
cout << "\nWhen to Use:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "• Define common interface for family" << endl;
cout << "• Force derived classes to implement" << endl;
cout << "• Enable polymorphic behavior" << endl;
cout << "• Decouple interface from implementation" << endl;
cout << endl;
cout << "============================================" << endl;
cout << "ABSTRACT CLASSES COMPLETE!" << endl;
cout << "============================================" << endl;
return 0;
}
// ============================================================
// EXERCISES:
// ============================================================
/*
* 1. Create an abstract Vehicle class:
* - Pure virtual: start(), stop(), accelerate(), brake()
* - Derived: Car, Motorcycle, Truck
* - Each with unique implementations
*
* 2. Create an abstract DataSource:
* - Pure virtual: connect(), read(), write(), close()
* - Derived: FileSource, DatabaseSource, NetworkSource
* - Use factory to create sources
*
* 3. Create a game character system:
* - Interface: Attackable, Movable, Damageable
* - Classes: Player, Enemy, NPC implementing interfaces
* - Demonstrate polymorphic behavior
*
* 4. Create an abstract Logger:
* - Pure virtual: log(), setLevel(), getLevel()
* - Derived: ConsoleLogger, FileLogger, NetworkLogger
* - Support log levels (Debug, Info, Warning, Error)
*/