In this lesson, you will learn
Polymorphism in C++ means the same entity (function or object) behaves differently in different scenarios.
For example: A person can be a father to someone, a husband, a boss, an employee, a son, a brother, or can have many other relationships with various people.
Here, a person represents the object, and his relationships display the object’s ability to be represented in many forms with totally different characteristics.
Polymorphism helps to write code consistently and efficiently.
For example, suppose you want to find the area of a circle and a rectangle.
So, you create a base class that will deal with the type of shape. This base class contains two derived classes, one for the circle and another for the rectangle.
Now, instead of declaring two separate functions with separate names in both the derived classes to calculate the area of each shape, you can declare one function in the base class and override it in the child classes to calculate the area.
In this way, you increase the code consistency using polymorphism.
Polymorphism in C++ is broadly classified into two categories based on their characteristic features as shown below
In compile-time polymorphism, an object is bound to its function call at compile-time. It means, there is no ambiguity at compile time about which function is linked to a particular call. This is also called early binding, static binding, or static linking.
The compile-time polymorphism can be achieved in two ways
Function overloading allows multiple functions with the same name to exist in the same scope, with each function having a different parameter list (either in the number or type of parameters). This helps in defining functions with similar purposes but different implementations.
#include<iostream> using namespace std; class Calculator { public: // Function to add two integers int add(int a, int b) { return a + b; } // Function to add three integers int add(int a, int b, int c) { return a + b + c; } // Function to add two double values double add(double a, double b) { return a + b; } }; int main() { Calculator calc; cout << "Sum of 10 and 20: " << calc.add(10, 20) << endl; // Calls add(int, int) cout << "Sum of 10, 20, and 30: " << calc.add(10, 20, 30) << endl; // Calls add(int, int, int) cout << "Sum of 5.5 and 6.5: " << calc.add(5.5, 6.5) << endl; // Calls add(double, double) return 0; }
Sum of 10 and 20: 30 Sum of 10, 20, and 30: 60 Sum of 5.5 and 6.5: 12
Operator overloading allows defining a new behavior for existing operators (like +
, -
, *
, etc.) when they are used with user-defined types. This enables you to use operators with custom objects in a natural way.
#include<iostream> using namespace std; class Complex { private: float real, imag; public: Complex(float r = 0, float i = 0) : real(r), imag(i) {} // Overload + operator Complex operator + (const Complex& other) { return Complex(real + other.real, imag + other.imag); } void display() { cout << real << " + " << imag << "i" << endl; } }; int main() { Complex c1(3.2, 4.5), c2(1.1, 2.3); Complex c3 = c1 + c2; c3.display(); return 0; }
Here, the + operator is overloaded to add two Complex objects by adding their real and imaginary parts separately.
#include<iostream> using namespace std; class Shape { public: // Function to calculate the area of a square double area(double side) { return side * side; } }; class Rectangle : public Shape { public: // Overloaded function to calculate the area of a rectangle double area(double length, double width) { return length * width; } }; int main() { Rectangle rect; // Calling the overloaded area functions using the derived class object cout<<"Area of rectangle with length 5 and width 10: " <<rect.area(5, 10) << endl; // Calls Rectangle::area(double, double) cout<<"Area of square with side 5: " <<rect.Shape::area(5) << endl; // Calls Shape::area(double) return 0; }
Area of rectangle with length 5 and width 10: 50 Area of square with side 5: 25
In C++, compile-time polymorphism typically involves function overloading (same function name with different parameters) or operator overloading, and it cannot involve the same function signature in both base and derived classes. If the same method with the same signature is defined in both base and derived classes, it is instead a case of function hiding rather than polymorphism.
Let me clarify this with an example. If you have a function with the same name, signature, and type in both base and derived classes (without using virtual
), then the derived class function will hide the base class function when accessed through a derived class object.
However, this does not fall under compile-time polymorphism or true polymorphism at all.
Here’s an example where the derived class function hides the base class function with the same method signature:
#include<iostream> using namespace std; class Base { public: // Function with the same name and signature in both base and derived classes void show() { cout << "Show from Base class" << endl; } }; class Derived : public Base { public: // Same function as in the base class (no virtual keyword) void show() { cout << "Show from Derived class" << endl; } }; int main() { Base base; Derived derived; // Calling the function through a Base class object base.show(); // Calls Base::show() // Calling the function through a Derived class object derived.show(); // Calls Derived::show(), hides Base::show() // Calling the base class using scope resolution operator derived.Base::show(); //Calls the base class show() return 0; }
Show from Base class Show from Derived class Show from Base class
In this example, derived.show() function call overrides the show() function defined in the base class. So, to call a specific function of a base class, you have to use the scope resolution (::) operator.
Function overriding in C++ occurs when two or more functions with the same name, arguments, and return type exist in base and derived classes.
If you create an object of the derived class and write code to access that member function, the member function in the derived class is only invoked; i.e., the member function of the derived class overrides the member function of the base class.
It refers to the linking of a function call to a particular class much later i.e. run time. Thus, it does not know which function will be invoked till an object makes the function call during a program execution.
This process is known as late or dynamic binding.
In C++, run-time polymorphism is achieved using a virtual function.
The scope resolution operator is fine to resolve the ambiguity but if you want to build a large or complex application on a large scale(which can be expanded in the future to add more features), then it would be very difficult to modify the code that implements the static binding or compile time polymorphism. So, there is a need for Run-Time polymorphism.