Polymorphism in C++ Programming
OOPs in C++
In this lesson, we will understand what is Polymorphism in C++ and how to implement them in programming along with some examples.
What is Polymorphism in C++?
The term Polymorphism has been derived from the words poly means many and morphism means form. In programming, polymorphism means to create many forms from one.
There are two types of polymorphism available in C++ and they are:
- Compile Time Polymorphism
- Runtime Polymorphism
Let's discuss each of them in detail one by one.
Compile Time Polymorphism
In C++, compile time polymorphism refers to the ability of the compiler to select the appropriate function or operator implementation to use at compile time, based on the types of the arguments that are passed to it. This is also known as static binding or early binding.
In C++, compile time polymorphism can be achieve using two ways:
- Function Overloading
- Operator Overloading
Function Overloading
Function Overloading is a process in which we declare a function that can perform a different task when provided with a different number of inputs.
For example, we will overload a function called area, which, when called with one argument, will display the area of a square. When the same method is called with two arguments, it will display the area of a rectangle.
Example of Function Overloading
#include <iostream>
using namespace std;
void area(int side)
{
int a;
a=side*side;
cout<<"Area of the square = "<<a<<endl;
}
void area(int l,int b)
{
int a;
a=l*b;
cout<<"Area of the rectangle = "<<a<<endl;
}
int main()
{
area(5);
area(10,4);
return 0;
}
Output
Area of the square = 25 Area of the rectangle = 40
In the above program, we declared two functions having the same name but with different numbers of arguments. When we call the area() function with one argument, it prints the square's area.
On the other hand, when we call the area() function with two arguments, it prints the rectangle's area.
Operator Overloading
Operator Overloading is a process in which we define a new action for an existing operator.
For example, + operator is used for adding two or more numbers. We can define a new action for the + operator so that it can add values stored inside two objects. Doing so means that we have overloaded the + operator.
To overload an operator in C++, we need to use a special function called an operator function. An operator function has the following syntax:
Operator Function Syntax
return_type operator operator_symbol(parameters)
{
// code here
}
Below are the types of operator overloading that we can do in C++:
- Unary Operator Overloading
- Binary Operator Overloading
- Stream Operator Overloading
Unary Operator Overloading
Unary operator overloading allow us to define the behavior of unary operators (operators that take a single operand like ++, --) when they are used with objects. For example, we can overload the increment operator (++) to increase the value of an object by 1.
Example
#include <iostream>
using namespace std;
class Numbers
{
private:
int x, y;
public:
Numbers(int a, int b)
{
x=a;
y=b;
}
void operator ++()
{
x++;
y++;
}
void display()
{
cout<<"x="<<x<<" y="<<y<<endl;
}
};
int main()
{
Numbers num(10,20);
num.display();
++num;
num.display();
return 0;
}
Output
x=10 y=20 x=11 y=21
Binary Operator Overloading
Binary operator overloading allow us to define the behavior of binary operators (operators that take two operands like +, *, etc.) when they are used with objects. For example, we can overload the plus operator (+) to add the value two objects.
Example
#include <iostream>
using namespace std;
class Numbers
{
private:
int x, y;
public:
Numbers()
{
x=0;
y=0;
}
Numbers(int a, int b)
{
x=a;
y=b;
}
Numbers operator +(Numbers &n)
{
Numbers z;
z.x = x+n.x;
z.y= y+ n.y;
return z;
}
void display()
{
cout<<"x="<<x<<" y="<<y<<endl;
}
};
int main()
{
Numbers num1(10,20);
Numbers num2(5,8);
Numbers total;
num1.display();
num2.display();
total=num1+num2;
total.display();
return 0;
}
Output
x=10 y=20 x=5 y=8 x=15 y=28
Stream Operator Overloading
In C++, we can overload the stream insertion << and stream extraction >> operators for our own custom classes. This can be useful if we want to be able to use the stream operators with our custom class in a way that is similar to how they are used with the built-in types.
Example of overloading both stream insertion << and extraction >> operators
#include <iostream>
using namespace std;
class Point
{
public:
int x, y;
Point()
{
x=0;
y=0;
}
Point(int a, int b)
{
x=a;
y=b;
}
friend ostream& operator <<(ostream &out, Point &p);
friend istream& operator >>(istream &in, Point &p);
};
ostream& operator <<(ostream &out, Point &p)
{
out << "(" << p.x << ", " << p.y << ")";
return out;
}
istream& operator >>(istream &in, Point &p)
{
in>>p.x>>p.y;
return in;
}
int main()
{
Point p;
cout<<"Enter values for x and y"<<endl;
cin>>p;
cout<<p;
return 0;
}
Output
Enter values for x and y 10 20 (10, 20)
To overload the insertion operator we have to declare a friend function inside the Point class. The return type of the function must be of type ostream references. To overload the insertion operator we need two arguments, ostream reference variable and reference variable of our custom class.
In same way, to overload the extraction operator we have to declare a friend function inside the Point class. The return type of the function must be of type istream references. To overload the extraction operator we need two arguments, istream reference variable and reference variable of our custom class as shown in the above example.
List of operators we can overload in C++
- Arithmetic operators: +, -, *, /, %
- Comparison operators: ==, !=, >, <, >=, <=
- Logical operators: &&, ||, !
- Bitwise operators: &, |, ^, ~, <<, >>
- Increment and decrement operators: ++, --
- Assignment operators: =, +=, -=, *=, /=, %=, <<=, >>=, &=, ^=, |=
- Member access operators: ., ->
- Function call operator: ()
- Array subscript operator: []
List of operators we can't overload in C++
- Scope resolution operator: ::
- Member selection operator: .
- Member selection through pointer to member operator: .*
- Ternary conditional operator: ?:
- sizeof
- typeid
- const_cast
- dynamic_cast
- reinterpret_cast
Runtime Polymorphism
In C++, runtime polymorphism is a programming technique where the behavior of a member function of a class is determined at runtime rather than compile time. This is also known as dynamic binding or late binding.
In C++, runtime polymorphism can be achieved using:
- Method Overriding through Virtual Function.
What is Method Overriding in C++
Method Overriding is a process used in inheritance in which a base class method is redeclared with a new body in a subclass.
Example of Method Overriding
#include <iostream>
using namespace std;
class Shape
{
public:
virtual void draw()
{
cout<<"Drawing a shape"<<endl;
}
};
class Circle : public Shape
{
public:
void draw()
{
cout<<"Drawing a circle"<<endl;
}
};
int main()
{
Circle c;
c.draw(); // prints "Drawing a circle"
return 0;
}
Output
Drawing a circle
In the above program, we have created a method called draw in the base class Shape that prints Drawing a shape on the screen. On the other hand, we have created another class called Circle that inherits the base class Shape. In the subclass Circle, we have overridden the method draw of the base class with a new body that prints Drawing a circle on the screen.
Virtual Function
In C++, a virtual function is a member function that is declared with the keyword virtual and can be overridden by a derived class. When a virtual function is called through a pointer to a base class, the compiler will determine at runtime which version of the function to execute based on the type of the object being pointed to.
Example
#include <iostream>
using namespace std;
class Shape
{
public:
virtual void draw()
{
cout<<"Drawing a shape"<<endl;
}
};
class Circle : public Shape
{
public:
void draw()
{
cout<<"Drawing a circle"<<endl;
}
};
int main()
{
Shape *s;
Circle c;
s=&c;
s->draw(); // prints "Drawing a circle"
return 0;
}
Output
Drawing a circle
In the above example, the draw function in the Circle class is an override of the virtual draw function in the Shape class. When the draw function is called through the pointer s to the base class Shape, the compiler will determine at runtime that the object pointed to is actually a Circle, and execute the draw function in the Circle class rather than the one in the Shape class. This is an example of runtime polymorphism.