Polymorphism means one name with many forms, in C++ polymorphism is a mechanism in which the same function, name, and operator can be used to operate on different input parameters. Polymorphism is one of the important features in object-oriented programming. A real-world example will be a single person who has multiple roles like father, brother, husband, friend, etc.
We have two types of polymorphism in C++
- Compile Time Polymorphism
- Runtime Polymorphism
The below diagram can give a clear view of polymorphism and its types:
Compile Time polymorphism
C++ provides compile-time polymorphism on operators and functions which is called operator overloading and function overloading respectively, compile-time polymorphism is also called early binding or static linkage, let’s explore both of them.
1.1 Function overloading
Function overloading is a polymorphism mechanism in which the same function name can be called with different parameters; the parameters should differ in terms of number or with different data types. Let us see an example:
#include<bits/stdc++.h> using namespace std; int add(int number1, int number2){ return (number1 + number2); } float add(double number1, double number2){ return (number1 + number2); } void main(){ cout<<add(5,10); cout<<add(5.1,10.2); }
We can see in the above example there are two functions with the same name as add, the first one with two integer parameters and the second one with two double parameters, if you compile this program in a C compiler it will generate an error saying function declaration with the same name, but in C++ this code will get compiled and on execution add(5,10) will call the function with integer definition and add(5.1,10.2) will call add with double definition. The compiler distinguishes the functions based on the mechanism called name mangling.
Name Mangling: Name mangling appends additional information to the functions having the same name, this additional information is based on mostly parameters of the function, every compiler may use its own technique for name mangling there is no hard and fast rule for name mangling. For the above example, the C++ compiler may do name mangling internally and the object code may look like:
int add_int(int number1,int number2); float add_double(double number1,double number2); void main() cout<<add_int(5,10); cout<<add_double(5.1,10.2); }
Let’s see some function overloading examples
Example 1
#include<bits/stdc++.h> using namespace std; int add(int number1, int number2,int number3 ){ return (number1 + number2 + number3); } int add(int number1, int number2){ return (number1 + number2); }
The above example has a different number of parameters for both functions, now the compiler calls the functions based on the number of parameters passed, the first function calls with 3 arguments, and the second with 2 arguments which call respective functions.
Example 2
using namespace std; int add(int number1, int number2){ return (number1 + number2); } float add(int number1, int number2){ return (number1 + number2); } void main(){ cout<<add(5,10); cout<<add(5,10); }
For the above example, the compiler will give a compilation error saying ambiguous function call because both functions have the same number of parameters with the same types though the return type is different function overloading mechanism does not differentiate function calls based on return types.
Example 3
#include<bits/stdc++.h> using namespace std; int add(int number1, int number2,int number3 = 5){ return (number1 + number2 + number3); } int add(int number1, int number2){ return (number1 + number2); } void main(){ cout<<add(5,10,15); cout<<add(5,10); }
For the above code, the C++ compiler will give compilation error ambiguous function declaration, though the first add function has three parameters the last one is a default parameter, so the compiler can’t decide which function should be called. The first function also can be called by passing two arguments and the second function also can be called by passing two arguments this creates ambiguity and the compiler gives an error during compilation.
Rules for function overloading
- The name of the functions should be the same.
- The number of parameters passed to the function should differ, default parameters are excluded or the parameters passed to functions should be of different data types (if the same numbers of parameters are passed).
- Data types like char, unsigned char, short are promoted to int, and Float is promoted to double.
- The function which differs only in return type will not support function overloading.
1.2 Operator overloading
Operator overloading is a mechanism in C++ which allows the usage of an operator on a user-defined class. For example, we can overload the “+” operator inside a class to add the contents of two objects and returns them to the third object.
#include<bits/stdc++.h> using namespace std; class Complex { private: int real; int imaginary; public: Complex (int r, int i) { real = r; imaginary = I; } Complex operator + (Complex &obj){ Complex result; result.real = real + obj.real; result.imaginary = imaginary + obj.imaginary; return result; } void print(){ cout<< real << “ imaginary ” << imaginary << endl; } } void main () { Complex c1(10,15),C2(20,25); Complex c3 = c1+c2; C3.print(); } Output: 30 imaginary 40
As you can see we have function Complex operator + (Complex &obj) this is an operator overloaded function which automatically gets called when two objects of Complex class are added like c3 = c1 + c2;
The syntax for the operator overloading function is as follows
<return type> operator <actual operator> (parameters)
The above example shows “+” operator used for operator overloading similarly we can overload other operators like – , *, /, <,>, = etc, but there are few operators which cannot be overloaded like
. (dot)
sizeof()
::
?:
Rules for operator overloading
- Operators which are supported by C++ can only be overloaded.
- The precedence of operators remains the same.
- At least one user-defined data type must be there, we cannot only use built-in data types.
- The overloaded operators should not hold default parameters except function call operator “()”
- The assignment “=”, the subscript “[]”, function call “()” and arrow operator “->” these operators must be defined as member functions, not the friend functions.
- Some operators like assignment “=”, address “&” and comma “,” are by default overloaded.
- The meaning of the operator which is overloaded should not be changed.
2.0 Runtime polymorphism
C++ provides runtime time polymorphism which can be achieved by function overriding; runtime polymorphism is also called late binding or dynamic binding. We say a function is overridden when a derived class has the same function name that is inside the base class, then we call the base class function as overridden. Let’s see an example:
#include<bits/stdc++.h> using namespace std; class Paint { public: virtual void show(){ cout<<”paint::show()”<< endl; } void draw(){ cout<<”paint::draw()”<< endl; } } class Circle : public Paint { public: void show(){ cout<<”circle::show()”<< endl; } void draw(){ cout<<”circle::draw()”<< endl; } } void main () { Paint *basePtr; Circle derivedObj; basePtr = &derivedObj; basePtr->show(); basePtr->draw(); return 0; } Output: circle::show() paint::draw()
As we can see basePtr->show() calls the derived class show function because show() function is made as a virtual function in the base class, virtual functions are called based on the object of the class assigned to the base class pointer whereas non-virtual function like draw() of base class will get called.
Virtual Function
A virtual function is a function in a base class that is declared using the keyword virtual which instructs the compiler to use a late-binding / dynamic binding mechanism instead of a static or early binding mechanism, with the help of virtual functions we can achieve run time polymorphism, the class which declares functions as virtual the compiler needs to create a virtual table to establish the dynamic calling
Pure Virtual Function
C++ provides a pure virtual function mechanism in which the virtual function in the base class is just the place holder and does not implement anything, but the derived classes have to implement them, pure virtual functions are declared like
virtual void show () = 0
They are assigned with 0; a class with all pure virtual functions is called an Abstract base class
Let’s summarize the topic with the advantages and disadvantages of polymorphism.
Advantages
- It reduces the burden of the programmer to remember the different function names for different parameters to be called; the programmer just calls the same function with the required parameters.
- Polymorphism can be applied to user-defined data types and classes which define customize operations on user-defined data types.
- Dynamic linking helps in reducing the code and increases reusability.
- Polymorphism helps in supporting data abstraction.
Disadvantages
- Polymorphism is found to be difficult for the developers to implement.
- Run time polymorphism increases the execution time.
- Runtime polymorphism is difficult to understand, it has less readability
- In case of operator overloading the developer can change the meaning of operator like if “+” is overloaded he can perform other than addition operation which compiler does recognize as error, in this way the basic meaning of operators can be changed.
- Runtime polymorphism requires the compiler to maintain a virtual table for dynamic binding, which amounts to extra storage.