Polymorphism is the ability of something to be displayed in multiple forms. In programming context, polymorphism means that some programming code or functionality or objects behave differently in different contexts or convey messages in different forms. Polymorphism occurs when there is a hierarchy of classes and they are related by inheritance.
Let’s take a real life scenario; a person at the same time can perform several duties as per demand, in the particular scenario. Such as, a man at a same time can serve as a father, as a husband, as a son, and as an employee. So, single person possess different behaviors in respective situations. This is the real life example of polymorphism. It is one of the important features of OOP (Object Oriented Programming).
The design patterns show how to use primitive techniques such as objects, inheritance, and polymorphism.
Definition of Polymorphism
Polymorphism is defined more or less same in different text books.
What virtual functions provide is called runtime polymorphism, and what templates offer is called compile time polymorphism or parametric polymorphism.
The C+ + Programming Language Third Edition by Bjarne Stroustrup
Polymorphism essentially means that a given function call can be bound to different implementations, depending on compile-time or runtime contextual issues.
Modern C++ Design: Generic Programming and Design Patterns Applied by Andrei Alexandrescu
Polymorphism is the third essential feature of an object-oriented programming language, after data abstraction and inheritance… Polymorphism allows improved code organization and readability as well as the creation of extensible programs that can be “grown” not only during the original creation of the project, but also when new features are desired.
Thinking in Java Fourth Edition by Bruce Eckel
Polymorphism simplifies the definitions of clients, decouples objects from each other, and lets them vary their relationships to each other at run-time. The ability to substitute objects of matching interface for one another at run-time.
Design Patterns: Elements of Reusable Object-Oriented Software by by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, Grady Booch
Polymorphism allows subclasses to distinguish themselves from one another…the term polymorphism is used to describe the situation in which a single statement can take on different definitions.
Calvin College
What are the types of Polymorphism in C++
In C++ Polymorphism is mainly divided into two types
1) Compile Time Polymorphism and
2) Runtime Polymorphism
Compile Time Polymorphism
This type of polymorphism is also known as static polymorphism and is achieved by overloading a function, method or an operator. Generally in C++, what templates offer is called compile time polymorphism or
parametric polymorphism.
What is Function Overloading?
When multiple functions are used at different places with same name but different parameters then these functions are known as overloaded function. Functions can be overloaded in two ways:
1) By change in number of arguments
2) By change in type of arguments
Let’s consider an example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #include <iostream.h> using namespace std; class mycplus { public: // function # 01: with 1 parameter of type int void function(int a) { cout << "value of a is " << a << endl; } // function # 02: having same name but a double parameter void function(double a) { cout << "value of a is " << a << endl; } // function # 03: with same name but 2 int parameters void function(int a, int b) { cout << "value of a and b is " << a << ", " << b << endl; } }; int main() { mycplus obj; // The call of function will depend on the type of parameters // passed //The first 'function with one int parameter' is called obj.function(3); // The second 'function with a double parameter' is called obj.function(6.456); // The third 'function with 2 int parameters' is called obj.function(8,71); return 0; } |
Output:
value of a is 3
value of a is 6.456
value of a and b is 8, 71
The above example perfectly explains the concept of function overloading, a single function/method named function acts differently in 3 different situations which is the property of polymorphism.
Operator Overloading
The second method of compile time polymorphism is operator overloading. For example, we can make the operator (‘+’) for string class to concatenate two strings. The general concept regarding “+” operator is that it is an addition operator whose task is to sum up two operands. But here in polymorphism, it will be used for a different purpose.
So, a single operator ‘+’ when placed between strings, concatenate them and when placed between integer operands, adds them. Let’s take an example of illustrating Operator Overloading using C++.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include<iostream> using namespace std; class Testop { private: int real, imag; public: Testop(int r = 0, int i =0): real(r), imag(i) {} // The following function will automatically called when '+' // is used with between two Testop objects Testop operator + (Testop const &obj) { Testop obj1; obj1.real = real + obj.real; obj1.imag = imag + obj.imag; return obj1; } void print() { cout << "The result of adding two complex numbers by operator overloading is " << real << " + i" << imag << endl; } }; int main() { Testop t1(12, 15), t2(1, 6); Testop t3 = t1 + t2; // call to "operator +" t3.print(); } |
Output:
The result of adding two complex numbers by operator overloading is 13 + i21
In above mentioned example the “+” operator is overloaded. Here the operator is made to perform addition of two complex numbers.
1 | Testop operator + (Testop const &obj) |
is called automatically when the operator “+” is used in the main function.
Here it is important to mention that, operator overloading can be done on both unary as well as binary operators.
Runtime polymorphism
The second type of polymorphism is runtime polymorphism. It can be achieved by using Function Overriding or method overriding i.e. the specific function to call will be determined at runtime based on object’s dynamic type. Generally in C++, what virtual functions provide is called runtime polymorphism.
What is Function Overriding?
Function overriding is giving another definition to an existing method with same parameters or we can say that a method has same prototype in base as well as derived class.
For example: when an inherited class in C++ has a different definition for one of functions of the base class then here the function of base class is said to be overridden.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #include <bits/stdc++.h> using namespace std; // Parent class class Base { public: void result() { cout << "Result method of Base class is called" << endl; } }; // Following is the Derived class having similar result() method as of //base class class Derive : public Base { public: // definition of a result method already exists in Base class void result() { cout << "The result method of derived class is called " << endl; } }; int main() { //instantiating object of Base class Base obj; //object of child class Derive obj1 = Derive(); // obj will call the result method in Base Class obj1.result(); // obj1 will override the result method in Base // and call the result method in Derive class Obj1.result(); return 0; } |
Output:
1 2 3 | Result method of Base class is called The result method of derived class is called |
In the above-mentioned example, the result () method of base class is overridden and redefined in the base class.
Runtime Polymorphism using Virtual Functions
Runtime polymorphism can also be achieved by virtual functions. A virtual Function is the member of base class and is overrides in the derived class. The syntax of a virtual function is to precede its declaration with keyword “virtual”.
Here are some c programs to demonstrate how virtual pointers, virtual tables and virtual functions work in C++
Virtual function actually tells the compiler to perform Dynamic Binding (resolves function call at runtime) on it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #include <iostream> using namespace std; class Polygon { protected: int w , h; public: void set_values (int x, int y) { w=x; h=y; } virtual int area () { return 0; } }; class Rect: public Polygon { public: int area () { return w * h; } }; class Tri: public Polygon { public: int area () { return (w * h / 2); } }; int main () { Rect rec; Tri trg; Polygon poly; Polygon * ppoly1 = &rec; Polygon * ppoly2 = &trg; Polygon * ppoly3 = &poly; ppoly1->set_values (11,25); ppoly2->set_values (11,25); ppoly3->set_values (11,25); cout << ppoly1->area() << '\n'; cout << ppoly2->area() << '\n'; cout << ppoly3->area() << '\n'; return 0; } |
Output:
1 2 | 275 137 |
In the above mentioned example, there are three classes i.e. Polygon, Rectangle and Triangle and all have the same functions set_values() and area() and same member variables w, h.
Function main declares three pointers, ppoly1, ppoly2 and ppoly3 to Polygon. The first two pointers are assigned the addresses of rec and trg that are objects of Rect and Tri. This type of assignment is valid, as both classes Rect and Tri are derived from Polygon.
*ppoly1 and *ppoly2 is dereferencing of both pointers and enables us to access the members of their pointed objects. To understand the concept, consider the following example,
1 | ppoly1->set_values (11,25); is equivalent to & rec.set_values (11,25); |
In base class, the member function “area” is declared as virtual and is later redefined in both of the derived classes Rect and Tri. So, it allows us to access the non-virtual members of derived classes via a reference of the base class. But if the keyword “virtual” is removed from the declaration of area in the base class, all three calls to area would return zero.
The virtual keyword allows the members of a derived class to be called appropriately who have the same name as one of the member of the base class. More accurately when the type of the pointer is a pointer to the parent class, which is pointing to an instance of the derived class, as you can see in the above example.