#include <iostream>
#include <memory>   // for std::unique_ptr
#include <vector>

// Base class: shape
class Shape {
public:
    // Virtual function, for demonstrating polymorphism
    virtual void draw() const {
        std::cout << "Draw a generic shape" << std::endl;
    }

    // Virtual destructor, ensures derived class objects are properly destroyed
    virtual ~Shape() {
        std::cout << "Shape destructor" << std::endl;
    }
};

// Derived class: circle
class Circle : public Shape {
public:
    // Override base class draw function
    void draw() const override {
        std::cout << "Draw a circle ⚪" << std::endl;
    }

    ~Circle() override {
        std::cout << "Circle destructor" << std::endl;
    }
};

// Derived class: rectangle
class Rectangle : public Shape {
public:
    void draw() const override {
        std::cout << "Draw a rectangle ◻️" << std::endl;
    }

    ~Rectangle() override {
        std::cout << "Rectangle destructor" << std::endl;
    }
};

// Derived class: triangle
class Triangle : public Shape {
public:
    void draw() const override {
        std::cout << "Draw a triangle ▲" << std::endl;
    }

    ~Triangle() override {
        std::cout << "Triangle destructor" << std::endl;
    }
};

int main() {
    std::cout << "=== C++ Polymorphism Demo Program ===" << std::endl;

    // Method 1: Use raw pointer array, call derived class methods through base class pointers
    std::cout << "\n--- Raw pointer method ---" << std::endl;
    Shape* shapes[] = {
        new Circle(),
        new Rectangle(),
        new Triangle()
    };

    for (int i = 0; i < 3; ++i) {
        shapes[i]->draw();   // Polymorphic call: calls corresponding draw() based on actual object type
    }

    // Release memory
    for (int i = 0; i < 3; ++i) {
        delete shapes[i];
    }

    // Method 2: Use smart pointers and standard containers (modern C++ style)
    std::cout << "\n--- Smart pointer method (recommended) ---" << std::endl;
    std::vector<std::unique_ptr<Shape>> shapeList;
    shapeList.push_back(std::make_unique<Circle>());
    shapeList.push_back(std::make_unique<Rectangle>());
    shapeList.push_back(std::make_unique<Triangle>());

    for (const auto& ptr : shapeList) {
        ptr->draw();   // Also exhibits polymorphism
    }

    // Method 3: Demonstrate polymorphism through references
    std::cout << "\n--- Reference method ---" << std::endl;
    Circle   c;
    Rectangle r;
    Triangle t;

    // Function accepts base class reference, polymorphism also occurs when calling virtual functions
    auto callDraw = [](const Shape& shape) {
        shape.draw();
    };

    callDraw(c);
    callDraw(r);
    callDraw(t);

    std::cout << "\nProgram ending, all objects will be automatically destructed (smart pointers and stack objects)" << std::endl;
    return 0;
}

results matching ""

    No results matching ""