Polymorphism is an object-oriented programming concept that refers to the ability of a variable, function, or object to take on multiple forms. In a programming language exhibiting polymorphism, class objects belonging to the same hierarchical tree (inherited from a common parent class) may have functions with the same name, but with different behaviors.
The classic example is of the Shape class and all the classes that are inherited from it, such as:
Rectangle
Triangle
Circle
Below is an example of Polymorphism:
class Shape{public:Shape(){}//defining a virtual function called Draw for shape classvirtual void Draw(){cout<<"Drawing a Shape"<<endl;}};class Rectangle: public Shape{public:Rectangle(){}//Draw function defined for Rectangle classvirtual void Draw(){cout<<"Drawing a Rectangle"<<endl;}};class Triangle: public Shape{public:Triangle(){}//Draw function defined for Triangle classvirtual void Draw(){cout<<"Drawing a Triangle"<<endl;}};class Circle: public Shape{public:Circle(){}//Draw function defined for Circle classvirtual void Draw(){cout<<"Drawing a Circle"<<endl;}};int main() {Shape *s;Triangle tri;Rectangle rec;Circle circ;// store the address of Rectangles = &rec;// call Rectangle Draw functions->Draw();// store the address of Triangles = &tri;// call Traingle Draw functions->Draw();// store the address of Circles = ˆ// call Circle Draw functions->Draw();return 0;}
In the example above,
We used virtual
keyword while defining the Draw()
functions as a virtual function is a member function which when declared in the base class can be re-defined (Overriden) by the derived classes.
At run time the compiler looks at the contents of the pointer *s
.
Since, the addresses of objects of tri
, rec
, and circ
are stored in *s
the respective Draw()
function is called.
As you can see, each of the child classes has a separate implementation for the function Draw()
. This is how polymorphism is generally used.
Compile time polymorphism
Example: Method overloading
Runtime polymorphism
Example: Method overriding
Let's see the implementation of compile time polymorphism and run-time polymorphism.
In the example below, We implement the method overloading which is a form of compile-time polymorphism in the Calculator
class. we overload add
method with different parameter lists.
#include <iostream>class Calculator {public:// Method to add two integersint add(int a, int b) {return a + b;}// Method to add three integersint add(int a, int b, int c) {return a + b + c;}};int main() {Calculator calc;// Method overloading - compiler determines which method to call based on argumentsstd::cout << "Result 1: " << calc.add(5, 10) << std::endl;std::cout << "Result 2: " << calc.add(5, 10, 15) << std::endl;return 0;}
In this example below, Animal
, Dog
, and Cat
classes demonstrate method overriding, a form of run-time polymorphism. Even though the reference is of type Animal
, the actual method called is determined by the type of object instantiated.
#include <iostream>using namespace std;class Animal {public:virtual void makeSound() {cout << "Animal makes a sound" << std::endl;}};class Dog : public Animal {public:void Sound() override {cout << "Dog barks" << std::endl;}};class Cat : public Animal {public:void makeSound() override {cout << "Cat meows" << std::endl;}};int main() {Animal* dog = new Dog(); // Runtime polymorphism - pointer of parent class, object of child classAnimal* cat = new Cat();dog->Sound(); // Calls Dog's makeSound methodcat->Sound(); // Calls Cat's makeSound methoddelete dog;delete cat;return 0;}
It helps programmers reuse code and classes once written, tested, and implemented.
A single variable name can be used to store variables of multiple data types (float
, double
, long
, int
, etc).
It helps compose powerful, complex abstractions from simpler ones.