C++ Polymorphism

Polymorphism in C++ is a fundamental concept in object-oriented programming (OOP) that allows objects of different classes to be treated as objects of a common base class. It enables a function, method, or operator to behave differently based on the object it is acting upon. The term “polymorphism” means “many forms,” and it allows the same interface to be used for different underlying forms (data types).

Types of Polymorphism

There are two Types of Polymorphism

1. Compile-time Polymorphism (Static Polymorphism)

This polymorphism is used in compile-time.

Function Overloading: Multiple functions can have the same name but different parameters (different type, number, or order of arguments).


#include <iostream>
using namespace std;

class Calculator {
public:
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }
};

int main() {
    Calculator cal; // Corrected class name and added semicolon
    int result1 = cal.add(15, 20); // Define result as an integer
    double result2 = cal.add(15.2, 20.5); // Define result as an double
    cout << "add two normal interger value = " << result1 << "\n"; // Printing the result
     cout << "add two decimal value = " << result2 << "\n"; // Printing the result
    return 0;
}

Output:

add two normal interger value = 35
add two decimal value = 35.7

Operator Overloading: The ability to redefine the way operators work for user-defined types (e.g., classes).


#include <iostream>
using namespace std;

class Calculator {
private:
    int value; // Value to store the number

public:
    // Constructor to initialize value
    Calculator(int v = 0) : value(v) {}

    // Overload the + operator to add two Calculator objects
    Calculator operator+(const Calculator& other) {
        // Create a new Calculator object that contains the sum of the values
        return Calculator(this->value + other.value);
    }

    // Function to display the value
    void display() const {
        cout << "Value: " << value << endl;
    }
};

int main() {
    Calculator cal1(10), cal2(20);
    
    // Use overloaded + operator to add two Calculator objects
    Calculator result = cal1 + cal2;

    // Display the result of addition
    result.display();  // Should display Value: 30

    return 0;
}

Explanation:

  • Operator Overloading: We overload the + operator for the Calculator class by defining a function operator+. This function takes another Calculator object and returns a new Calculator object that holds the sum of the value of both objects.
  • Constructor: The constructor Calculator(int v = 0) is used to initialize the value.
  • Display Function: display() is used to print the value stored inside a Calculator object.
  • In main(), we create two Calculator objects (cal1 and cal2), use the overloaded + operator to add them, and then print the result.

Output:

Value: 30

2. Run-time Polymorphism (Dynamic Polymorphism)

This type of polymorphism is resolved at runtime, which requires the use of inheritance and virtual functions.
The key idea is that a base class reference (or pointer) can point to objects of derived classes, and the appropriate method is called based on the actual object type, not the type of the reference.


#include <iostream>
using namespace std;

class Shape {
public:
    // Virtual function to be overridden by derived classes
    virtual void draw() {
        cout << "Drawing Shape\n";
    }

    // Virtual destructor for proper cleanup in case of polymorphism
    virtual ~Shape() {
        cout << "Shape Destructor\n";
    }
};

class Circle : public Shape {
public:
    // Override the draw function in the derived class
    void draw() override {
        cout << "Drawing Circle\n";
    }

    // Destructor for the derived class
    ~Circle() override {
        cout << "Circle Destructor\n";
    }
};

class Square : public Shape {
public:
    // Override the draw function in the derived class
    void draw() override {
        cout << "Drawing Square\n";
    }

    // Destructor for the derived class
    ~Square() override {
        cout << "Square Destructor\n";
    }
};

int main() {
    // Create base class pointer
    Shape* shape1 = new Circle();  // Point to Circle object
    Shape* shape2 = new Square();  // Point to Square object

    // Call draw function. It will call the appropriate function based on the object type.
    shape1->draw();  // Output: Drawing Circle
    shape2->draw();  // Output: Drawing Square

    // Delete the objects (virtual destructors will be called automatically)
    delete shape1;
    delete shape2;

    return 0;
}

Explanation:

  • draw() is a virtual function in the base class Shape. This means derived classes can override this function to provide their own behavior.
  • virtual ~Shape() is a virtual destructor. Virtual destructors are important when using pointers to base class objects, as they ensure that the destructors of derived classes are called properly (avoiding memory leaks).
  • Both Circle and Square classes override the draw() function to provide their own implementation.

Output:

Drawing Circle
Drawing Square
Circle Destructor
Shape Destructor
Square Destructor
Shape Destructor

Advantages of Polymorphism

Code Reusability: It allows one function to be reused for different types of objects.

Extensibility: You can add new derived classes without modifying the base class code.

Flexibility: It lets you treat different types of objects in a uniform way, simplifying code.