cpp

Polymorphism

04_Polymorphismāš™ļø
/**
 * ============================================================
 * C++ POLYMORPHISM
 * ============================================================
 * 
 * This file covers:
 * - Compile-time polymorphism (overloading)
 * - Runtime polymorphism (virtual functions)
 * - Virtual functions and vtable
 * - Pure virtual functions
 * - Abstract classes
 * 
 * Compile: g++ -std=c++17 -Wall 01_polymorphism.cpp -o polymorphism
 * Run: ./polymorphism
 * 
 * ============================================================
 */

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <cmath>

using namespace std;

// ============================================================
// PART 1: COMPILE-TIME POLYMORPHISM (FUNCTION OVERLOADING)
// ============================================================

class Calculator {
public:
    // Same name, different parameters = overloading
    int add(int a, int b) {
        return a + b;
    }
    
    double add(double a, double b) {
        return a + b;
    }
    
    int add(int a, int b, int c) {
        return a + b + c;
    }
    
    string add(const string& a, const string& b) {
        return a + b;
    }
};

// ============================================================
// PART 2: RUNTIME POLYMORPHISM (VIRTUAL FUNCTIONS)
// ============================================================

class Shape {
protected:
    string name;
    
public:
    Shape(const string& n) : name(n) {}
    
    virtual ~Shape() {
        cout << "  Shape destructor: " << name << endl;
    }
    
    // Virtual function - can be overridden
    virtual double area() const {
        return 0.0;
    }
    
    virtual double perimeter() const {
        return 0.0;
    }
    
    // Non-virtual function
    string getName() const {
        return name;
    }
    
    virtual void draw() const {
        cout << "Drawing " << name << endl;
    }
};

class Circle : public Shape {
private:
    double radius;
    
public:
    Circle(double r) : Shape("Circle"), radius(r) {}
    
    ~Circle() {
        cout << "  Circle destructor" << endl;
    }
    
    // Override 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 Circle with radius " << radius << endl;
    }
};

class Rectangle : public Shape {
private:
    double width, height;
    
public:
    Rectangle(double w, double h) : Shape("Rectangle"), width(w), height(h) {}
    
    ~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 Rectangle " << width << "x" << height << endl;
    }
};

class Triangle : public Shape {
private:
    double a, b, c;  // Three sides
    
public:
    Triangle(double s1, double s2, double s3) 
        : Shape("Triangle"), a(s1), b(s2), c(s3) {}
    
    ~Triangle() {
        cout << "  Triangle destructor" << endl;
    }
    
    double area() const override {
        // Heron's formula
        double s = (a + b + c) / 2;
        return sqrt(s * (s-a) * (s-b) * (s-c));
    }
    
    double perimeter() const override {
        return a + b + c;
    }
    
    void draw() const override {
        cout << "Drawing Triangle with sides " << a << ", " << b << ", " << c << endl;
    }
};

// ============================================================
// PART 3: ABSTRACT CLASS (PURE VIRTUAL)
// ============================================================

// Abstract class - cannot be instantiated
class Animal {
protected:
    string name;
    
public:
    Animal(const string& n) : name(n) {}
    
    virtual ~Animal() = default;
    
    // Pure virtual function - MUST be overridden
    virtual void speak() const = 0;
    virtual void move() const = 0;
    
    // Regular virtual function
    virtual void eat() const {
        cout << name << " is eating." << endl;
    }
    
    string getName() const { return name; }
};

class Dog : public Animal {
public:
    Dog(const string& n) : Animal(n) {}
    
    void speak() const override {
        cout << name << " says: Woof!" << endl;
    }
    
    void move() const override {
        cout << name << " runs on four legs." << endl;
    }
};

class Cat : public Animal {
public:
    Cat(const string& n) : Animal(n) {}
    
    void speak() const override {
        cout << name << " says: Meow!" << endl;
    }
    
    void move() const override {
        cout << name << " walks gracefully." << endl;
    }
};

class Bird : public Animal {
public:
    Bird(const string& n) : Animal(n) {}
    
    void speak() const override {
        cout << name << " says: Tweet!" << endl;
    }
    
    void move() const override {
        cout << name << " flies in the sky." << endl;
    }
};

// ============================================================
// PART 4: INTERFACE PATTERN
// ============================================================

// Interface - all pure virtual functions
class Drawable {
public:
    virtual ~Drawable() = default;
    virtual void draw() const = 0;
    virtual void resize(double factor) = 0;
};

class Serializable {
public:
    virtual ~Serializable() = default;
    virtual string serialize() const = 0;
    virtual void deserialize(const string& data) = 0;
};

// Class implementing multiple interfaces
class GameSprite : public Drawable, public Serializable {
private:
    int x, y;
    int width, height;
    string texture;
    
public:
    GameSprite(int x, int y, int w, int h, const string& tex)
        : x(x), y(y), width(w), height(h), texture(tex) {}
    
    // Drawable interface
    void draw() const override {
        cout << "Drawing sprite '" << texture << "' at (" << x << "," << y << ")" << endl;
    }
    
    void resize(double factor) override {
        width = static_cast<int>(width * factor);
        height = static_cast<int>(height * factor);
        cout << "Resized to " << width << "x" << height << endl;
    }
    
    // Serializable interface
    string serialize() const override {
        return "Sprite:" + texture + "," + to_string(x) + "," + to_string(y);
    }
    
    void deserialize(const string& data) override {
        cout << "Deserializing: " << data << endl;
        // Parse data and set values...
    }
};

// ============================================================
// PART 5: POLYMORPHISM WITH POINTERS/REFERENCES
// ============================================================

void makeAnimalSpeak(const Animal& animal) {
    // Works with any Animal subclass!
    animal.speak();
}

void processShapes(const vector<unique_ptr<Shape>>& shapes) {
    double totalArea = 0;
    
    for (const auto& shape : shapes) {
        shape->draw();
        cout << "  Area: " << shape->area() << endl;
        totalArea += shape->area();
    }
    
    cout << "Total area: " << totalArea << endl;
}

// ============================================================
// MAIN FUNCTION
// ============================================================

int main() {
    cout << "============================================" << endl;
    cout << "     C++ POLYMORPHISM" << endl;
    cout << "============================================" << endl << endl;

    // ========================================================
    // DEMO 1: Compile-Time Polymorphism (Overloading)
    // ========================================================
    
    cout << "--- DEMO 1: FUNCTION OVERLOADING ---" << endl << endl;
    
    Calculator calc;
    
    cout << "calc.add(5, 3) = " << calc.add(5, 3) << endl;
    cout << "calc.add(2.5, 3.5) = " << calc.add(2.5, 3.5) << endl;
    cout << "calc.add(1, 2, 3) = " << calc.add(1, 2, 3) << endl;
    cout << "calc.add(\"Hello \", \"World\") = " << calc.add("Hello ", "World") << endl;
    
    cout << "\nšŸ’” Resolved at compile time based on parameter types" << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 2: Runtime Polymorphism (Virtual Functions)
    // ========================================================
    
    cout << "--- DEMO 2: VIRTUAL FUNCTIONS ---" << endl << endl;
    
    Circle circle(5.0);
    Rectangle rectangle(4.0, 6.0);
    Triangle triangle(3.0, 4.0, 5.0);
    
    // Using base class pointer
    Shape* shapes[] = {&circle, &rectangle, &triangle};
    
    cout << "Processing shapes through Shape* pointer:" << endl;
    for (Shape* s : shapes) {
        s->draw();
        cout << "  Area: " << s->area() << endl;
        cout << "  Perimeter: " << s->perimeter() << endl;
        cout << endl;
    }
    
    cout << "šŸ’” Correct method called based on actual object type!" << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 3: Without Virtual (Static Binding)
    // ========================================================
    
    cout << "--- DEMO 3: VIRTUAL VS NON-VIRTUAL ---" << endl << endl;
    
    class Base {
    public:
        void nonVirtual() { cout << "Base::nonVirtual" << endl; }
        virtual void isVirtual() { cout << "Base::isVirtual" << endl; }
    };
    
    class Derived : public Base {
    public:
        void nonVirtual() { cout << "Derived::nonVirtual" << endl; }
        void isVirtual() override { cout << "Derived::isVirtual" << endl; }
    };
    
    Derived d;
    Base* ptr = &d;
    
    cout << "Calling through Base* ptr to Derived object:" << endl;
    ptr->nonVirtual();  // Calls Base version (static binding)
    ptr->isVirtual();   // Calls Derived version (dynamic binding)
    
    cout << endl;

    // ========================================================
    // DEMO 4: Abstract Class
    // ========================================================
    
    cout << "--- DEMO 4: ABSTRACT CLASS ---" << endl << endl;
    
    // Animal animal("Test");  // ERROR: can't instantiate abstract class
    
    Dog dog("Buddy");
    Cat cat("Whiskers");
    Bird bird("Tweety");
    
    // Array of Animal pointers
    Animal* animals[] = {&dog, &cat, &bird};
    
    cout << "Making all animals speak and move:" << endl;
    for (Animal* a : animals) {
        a->speak();
        a->move();
        a->eat();
        cout << endl;
    }
    
    // Using reference
    cout << "Using reference parameter:" << endl;
    makeAnimalSpeak(dog);
    makeAnimalSpeak(cat);
    makeAnimalSpeak(bird);
    
    cout << endl;

    // ========================================================
    // DEMO 5: Smart Pointers and Polymorphism
    // ========================================================
    
    cout << "--- DEMO 5: SMART POINTERS ---" << endl << endl;
    
    vector<unique_ptr<Shape>> shapeCollection;
    
    shapeCollection.push_back(make_unique<Circle>(3.0));
    shapeCollection.push_back(make_unique<Rectangle>(2.0, 4.0));
    shapeCollection.push_back(make_unique<Triangle>(3.0, 4.0, 5.0));
    shapeCollection.push_back(make_unique<Circle>(1.5));
    
    processShapes(shapeCollection);
    
    cout << "\nShapes being destroyed:" << endl;
    shapeCollection.clear();  // Proper cleanup with virtual destructor
    
    cout << endl;

    // ========================================================
    // DEMO 6: Interface Implementation
    // ========================================================
    
    cout << "--- DEMO 6: INTERFACES ---" << endl << endl;
    
    GameSprite sprite(100, 200, 64, 64, "player.png");
    
    // Use as Drawable
    Drawable* drawable = &sprite;
    drawable->draw();
    drawable->resize(2.0);
    
    // Use as Serializable
    Serializable* serializable = &sprite;
    cout << "Serialized: " << serializable->serialize() << endl;
    
    cout << endl;

    // ========================================================
    // POLYMORPHISM SUMMARY
    // ========================================================
    
    cout << "--- POLYMORPHISM CONCEPTS ---" << endl << endl;
    
    cout << "Compile-Time (Static) Polymorphism:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "• Function overloading" << endl;
    cout << "• Operator overloading" << endl;
    cout << "• Templates" << endl;
    cout << "• Resolved at compile time" << endl;
    
    cout << "\nRuntime (Dynamic) Polymorphism:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "• Virtual functions" << endl;
    cout << "• Pure virtual (= 0)" << endl;
    cout << "• Resolved at runtime via vtable" << endl;
    
    cout << "\nKey Points:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "• Use 'virtual' for runtime polymorphism" << endl;
    cout << "• Use 'override' to catch errors" << endl;
    cout << "• Always virtual destructor in base class" << endl;
    cout << "• Pure virtual (= 0) makes class abstract" << endl;
    cout << "• Access through pointer/reference" << endl;
    
    cout << endl;

    cout << "============================================" << endl;
    cout << "POLYMORPHISM SUMMARY:" << endl;
    cout << "============================================" << endl;
    cout << "• virtual: enable runtime polymorphism" << endl;
    cout << "• override: mark overridden functions" << endl;
    cout << "• = 0: pure virtual (abstract)" << endl;
    cout << "• Use base class pointers/references" << endl;
    cout << "• Virtual destructor is essential" << endl;
    cout << "============================================" << endl;

    return 0;
}

// ============================================================
// EXERCISES:
// ============================================================
/*
 * 1. Create a Payment hierarchy:
 *    - Abstract PaymentMethod with processPayment()
 *    - Derived: CreditCard, DebitCard, PayPal, Bitcoin
 *    - Each with different processing logic
 * 
 * 2. Create a Media Player:
 *    - Abstract MediaFile with play(), pause(), stop()
 *    - Derived: AudioFile, VideoFile, StreamingMedia
 *    - Process a playlist of different media types
 * 
 * 3. Create a GUI Widget system:
 *    - Abstract Widget with draw(), handleClick()
 *    - Derived: Button, TextBox, CheckBox, Slider
 *    - Container that holds multiple widgets
 * 
 * 4. Create a Document system:
 *    - Interface: Printable, Saveable, Searchable
 *    - Classes implementing multiple interfaces
 *    - Use polymorphism to process documents
 */
Polymorphism - C++ Tutorial | DeepML