Skip to content

C++ Polymorphism

Overview

Polymorphism allows objects of different classes to be treated as objects of a common superclass. The most common use of polymorphism is when a parent class reference is used to refer to a child class object.

Compile-time Polymorphism (Function Overloading)

cpp
#include <iostream>

class Calculator {
public:
    // Function overloading
    int add(int a, int b) {
        return a + b;
    }
    
    double add(double a, double b) {
        return a + b;
    }
    
    std::string add(const std::string& a, const std::string& b) {
        return a + b;
    }
    
    int add(int a, int b, int c) {
        return a + b + c;
    }
};

int main() {
    Calculator calc;
    
    std::cout << "Int addition: " << calc.add(5, 3) << std::endl;
    std::cout << "Double addition: " << calc.add(2.5, 3.7) << std::endl;
    std::cout << "String addition: " << calc.add("Hello", "World") << std::endl;
    std::cout << "Three numbers: " << calc.add(1, 2, 3) << std::endl;
    
    return 0;
}

Operator Overloading

cpp
#include <iostream>

class Complex {
private:
    double real, imag;
    
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
    
    // Overload + operator
    Complex operator+(const Complex& other) {
        return Complex(real + other.real, imag + other.imag);
    }
    
    // Overload << operator for output
    friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
        os << c.real;
        if (c.imag >= 0) os << "+";
        os << c.imag << "i";
        return os;
    }
    
    // Overload == operator
    bool operator==(const Complex& other) {
        return real == other.real && imag == other.imag;
    }
};

int main() {
    Complex c1(3.0, 4.0);
    Complex c2(1.0, 2.0);
    Complex c3 = c1 + c2;
    
    std::cout << "c1: " << c1 << std::endl;
    std::cout << "c2: " << c2 << std::endl;
    std::cout << "c1 + c2: " << c3 << std::endl;
    
    if (c1 == Complex(3.0, 4.0)) {
        std::cout << "c1 equals (3.0, 4.0)" << std::endl;
    }
    
    return 0;
}

Runtime Polymorphism (Virtual Functions)

cpp
#include <iostream>
#include <vector>

class Animal {
public:
    // Virtual function
    virtual void makeSound() {
        std::cout << "Some generic animal sound" << std::endl;
    }
    
    // Pure virtual function (makes class abstract)
    virtual void move() = 0;
    
    virtual ~Animal() {}  // Virtual destructor
};

class Dog : public Animal {
public:
    void makeSound() override {
        std::cout << "Woof! Woof!" << std::endl;
    }
    
    void move() override {
        std::cout << "Dog is running" << std::endl;
    }
};

class Cat : public Animal {
public:
    void makeSound() override {
        std::cout << "Meow! Meow!" << std::endl;
    }
    
    void move() override {
        std::cout << "Cat is walking gracefully" << std::endl;
    }
};

class Bird : public Animal {
public:
    void makeSound() override {
        std::cout << "Tweet! Tweet!" << std::endl;
    }
    
    void move() override {
        std::cout << "Bird is flying" << std::endl;
    }
};

int main() {
    // Array of base class pointers
    std::vector<Animal*> animals;
    animals.push_back(new Dog());
    animals.push_back(new Cat());
    animals.push_back(new Bird());
    
    // Polymorphic behavior
    for (Animal* animal : animals) {
        animal->makeSound();
        animal->move();
        std::cout << "---" << std::endl;
    }
    
    // Clean up
    for (Animal* animal : animals) {
        delete animal;
    }
    
    return 0;
}

Abstract Classes and Interfaces

cpp
#include <iostream>

// Abstract class (interface)
class Drawable {
public:
    // Pure virtual functions
    virtual void draw() = 0;
    virtual void resize(double factor) = 0;
    virtual ~Drawable() {}
};

class Shape : public Drawable {
protected:
    double x, y;  // Position
    
public:
    Shape(double x_pos, double y_pos) : x(x_pos), y(y_pos) {}
    
    void move(double dx, double dy) {
        x += dx;
        y += dy;
        std::cout << "Shape moved to (" << x << ", " << y << ")" << std::endl;
    }
};

class Circle : public Shape {
private:
    double radius;
    
public:
    Circle(double x, double y, double r) : Shape(x, y), radius(r) {}
    
    void draw() override {
        std::cout << "Drawing circle at (" << x << ", " << y 
                  << ") with radius " << radius << std::endl;
    }
    
    void resize(double factor) override {
        radius *= factor;
        std::cout << "Circle resized to radius " << radius << std::endl;
    }
};

class Rectangle : public Shape {
private:
    double width, height;
    
public:
    Rectangle(double x, double y, double w, double h) 
        : Shape(x, y), width(w), height(h) {}
    
    void draw() override {
        std::cout << "Drawing rectangle at (" << x << ", " << y 
                  << ") with width " << width << " and height " << height << std::endl;
    }
    
    void resize(double factor) override {
        width *= factor;
        height *= factor;
        std::cout << "Rectangle resized to " << width << " x " << height << std::endl;
    }
};

int main() {
    Drawable* shapes[2];
    shapes[0] = new Circle(10, 20, 5.0);
    shapes[1] = new Rectangle(30, 40, 8.0, 6.0);
    
    for (int i = 0; i < 2; i++) {
        shapes[i]->draw();
        shapes[i]->resize(1.5);
        shapes[i]->draw();
        std::cout << "---" << std::endl;
    }
    
    // Clean up
    for (int i = 0; i < 2; i++) {
        delete shapes[i];
    }
    
    return 0;
}

Content is for learning and research only.