WHAT IS POLYMORPHISM:


Polymorphism, fundamental in Object-Oriented Programming (OOP), is essential for a language to claim OOP support. If a language lacks polymorphism, it's classified as Object-Based rather than fully Object-Oriented.

The term "polymorphism" originates from the Greek for "Many Forms." It enables a single interface to interact with various classes of actions. For instance, consider an automobile's steering wheel as an interface; regardless of the steering mechanism type, the wheel functions similarly. Thus, turning the wheel left consistently results in the car steering left, irrespective of the steering mechanism. This uniform interface facilitates driving different cars once the steering operation is understood.

Christopher Strachey identified the concept of polymorphism in 1967, further developed by Hindley and Milner. Understanding the implementation of polymorphism isn't crucial; grasping its operational mechanism is. C++ provides methods for determining object and expression types during runtime, contrasting with compile-time determinations. In essence, polymorphism defers the decision of which function to execute until runtime, termed late binding. This dynamic decision-making is the essence of polymorphism.

Polymorphism, denoting multiple forms, typically occurs in a class hierarchy through inheritance. In C++, polymorphism implies that invoking a member function results in the execution of a different function depending on the invoking object's type.

Consider the following example demonstrating hierarchical inheritance:

POLYMORPHISM - C++ Copy to Clipboard  
#include <iostream>
using namespace std;

class Shape { // base class.
protected:
    int width, height;

public:
    Shape(int a = 0, int b = 0) { // Constructor.
        width = a;
        height = b;
    }

    int area() { // Basic function to show the outcome.
        cout << "Shape class area | Base Class | :" << endl;
        return 0;
    }
};

class Rectangle : public Shape { // Derived class.
public:
    Rectangle(int a = 0, int b = 0) : Shape(a, b) {} // constructor.
    int area() { // Basic function to show the outcome.
        cout << "Rectangle class area | Derived Class | :" << endl;
        return (width * height);
    }
};

class Triangle : public Shape { // Derived class.
public:
    Triangle(int a = 0, int b = 0) : Shape(a, b) {} // Constructor.
    int area() { // Basic function to show the outcome.                                     
        cout << "Triangle class area | Derived Class | :" << endl;
        return (width * height / 2);
    }
};

int main()
{
    Shape *shape;
    Rectangle rec(10, 7);
    Triangle tri(10, 5);

    shape = &rec; 

    // Invoke the area function using the base class pointer for a rectangle.
    shape->area(); 

    shape = &tri;  

    // Invoke the area function using the base class pointer for a triangle.
    shape->area();

    return 0;
}

The initial output of the program is incorrect due to static resolution of the function call, where the compiler fixes the function call during compilation, termed early binding.

By modifying the area()declaration in the "Shape class" with the keyword virtual, dynamic function invocation is achieved, ensuring the appropriate function call based on the object's content rather than its type.

POLYMORPHISM - C++ Copy to Clipboard  
#include <iostream>
using namespace std;

class Shape { // base class.
protected:
    int width, height;

public:
    Shape(int a = 0, int b = 0) { // Constructor.
        width = a;
        height = b;
    }

    virtual int area() { // Basic function to show the outcome.
        cout << "Shape class area | Base Class | :" << endl;
        return 0;
    }
};

class Rectangle : public Shape { // Derived class.
public:
    Rectangle(int a = 0, int b = 0) : Shape(a, b) {} // constructor.
    int area() { // Basic function to show the outcome.
        cout << "Rectangle class area | Derived Class | :" << endl;
        return (width * height);
    }
};

class Triangle : public Shape { // Derived class.
public:
    Triangle(int a = 0, int b = 0) : Shape(a, b) {} // Constructor.
    int area() { // Basic function to show the outcome.                                     
        cout << "Triangle class area | Derived Class | :" << endl;
        return (width * height / 2);
    }
};

int main()
{
    Shape *shape;
    Rectangle rec(10, 7);
    Triangle tri(10, 5);

    shape = &rec; 

    // Invoke the area function using the base class pointer for a rectangle.
    shape->area(); 

    shape = &tri;  

    // Invoke the area function using the base class pointer for a triangle.
    shape->area();

    return 0;
}

Compile-time polymorphism occurs when the compiler selects the appropriate function for a specific call during compilation. This encompasses method overloading and method overriding.

Method Overloading:

Method overloading entails defining the same method multiple times within a class, differing in the number or types of parameters. Consider the following example:

METHOD OVERLOADING - C++ Copy to Clipboard  
#include<iostream>
using namespace std;
 
class Math {
public:
    void result(int x, int y) {
        cout<<" The result is : "<<x+y;
    }

    void result(int x, int y, int z) {
        cout<<" The result is: "<<x+y+z;
    }
};

int main() {
    Math obj;

    obj.result(10,15);
    cout<<endl;

    obj.result(10,5,15);
    return 0;    
}
Method Overriding:

When a derived class provides a definition for a base class's method, the method is said to be overridden. This mechanism enables customization of inherited functionality. Consider the following example:

METHOD OVERRIDING - C++ Copy to Clipboard  
#include<iostream>
using namespace std;
 
class Base {
public:
    void output() {
        cout<<" Its Base Class: "<<endl;
    }
};

class Derived: public Base {
public:
    void output() {
        cout<<" Its Derived Class: "<<endl;
    }
};

int main() {
    Base parent;
    Derived child;

    parent.output();
    cout<<endl;

    child.output();

    return 0;    
}

Run-time polymorphism involves determining which object's method to invoke during runtime, rather than compile time. This is achieved by defining virtual methods in the base class and overriding them in derived classes.