The word polymorphism means having many forms. In C++, polymorphism concept can be applied to functions and operators. A single function name can work differently in different situations. Similarly, an operator works different when used in different context.
- Same function or operator can behave differently depending on the context or object it is used with.
- Achieved through function overloading and function overriding, improving code reusability and flexibility.
Types of Polymorphism
In C++, polymorphism is mainly divided into two types
- Compile-Time Polymorphism: The function or operator is decided at the time of compilation based on how it is called.
- Run-Time Polymorphism: The function is decided while the program is running based on the object being used.

Compile-Time Polymorphism
Compile-time polymorphism is also known as static polymorphism or early binding. In this type of polymorphism, the compiler decides which function or operator to call during compilation. It is achieved using:
1. Function Overloading
Function Overloading allows multiple functions to have the same name but different parameters. The difference can be in:
- Number of parameters
- Type of parameters
Explanation: An add() function is used to perform addition of two numbers, and it works differently for integers and floating-point values. The compiler selects the correct function based on the arguments passed during the function call.
#include <bits/stdc++.h>
using namespace std;
class Geeks {
public:
// Function to add two integers
void add(int a, int b) {
cout << "Integer Sum = " << a + b
<< endl;
}
// Function to add two floating point values
void add(double a, double b) {
cout << "Float Sum = " << a + b
<< endl ;
}
};
int main() {
Geeks gfg;
// add() called with int values
gfg.add(10, 2);
// add() called with double value
gfg.add(5.3, 6.2);
return 0;
}
Output
Integer Sum = 12 Float Sum = 11.5
- The class Geeks contains two overloaded functions named add(), one for integers and one for double values.
- When add(10, 2) is called, the integer version is executed; when add(5.3, 6.2) is called, the double version is executed.
- The compiler decides which function to call at compile time based on the argument types, demonstrating function overloading.
2. Operator Overloading
Operator overloading allows operators like +, -, and * to work with user-defined data types such as classes. It lets us define how an operator should behave when used with custom objects.
For example:
- The + operator adds two integers.
- The same + operator can concatenate two strings.
- We can also define how + should behave for custom classes.
This makes code more readable and natural.
Note: Most operators in C++ can be overloaded, but operators such as ::, ., .*, ?:, and sizeof cannot be overloaded because they are essential to the core functionality of the language.
Example: A
Complexnumber class is used to represent numbers having real and imaginary parts. The+operator is overloaded to add two complex numbers directly.
#include <iostream>
using namespace std;
class Complex {
public:
int real, imag;
Complex(int r, int i) :
real(r), imag(i) {}
// Overloading the '+' operator
Complex operator+(const Complex& obj) {
return Complex(real + obj.real, imag + obj.imag);
}
};
int main() {
Complex c1(10, 5), c2(2, 4);
// Adding c1 and c2 using + operator
Complex c3 = c1 + c2;
cout << c3.real << " + i" << c3.imag;
return 0;
}
Output
12 + i9
- The class Complex stores real and imaginary parts and defines an overloaded + operator to add two Complex objects.
- When c1 + c2 is used, the overloaded + function is automatically called and adds both real and imaginary values separately.
- The result is stored in c3, which represents the sum of the two complex numbers and is then printed.
2. Runtime Polymorphism
Runtime polymorphism is also known as dynamic polymorphism or late binding. In this type, the function call is resolved during program execution instead of compilation. It is achieved using:
Real-Life Illustration of Polymorphism
Different animals represent polymorphism, where the same method speak() produces different outputs such as Bark, Meow, and Moo depending on the object calling the method.

In above Diagram:
- A Dog object says Bark
- A Cat object says Meow
- A Cow object says Moo
Even though all animals use the same function name speak(), the behavior changes according to the object.
1. Function Overriding
Function Overriding occurs when a derived class defines one or more member functions of the base class. That base function is said to be overridden. The base class function must be declared as virtual function for runtime polymorphism to happen.
Example: A base class pointer is used to point to a derived class object. The
display()function is overridden in the derived class to demonstrate runtime polymorphism using a virtual function.
#include <bits/stdc++.h>
using namespace std;
class Base {
public:
// Virtual function
virtual void display() {
cout << "Base class function";
}
};
class Derived : public Base {
public:
// Overriding the base class function
void display() override {
cout << "Derived class function";
}
};
int main() {
// Creating a pointer of type Base
Base* basePtr;
// Creating an object of Derived class
Derived derivedObj;
// Pointing base class pointer to
// derived class object
basePtr = &derivedObj;
// Calling the display function
// using base class pointer
basePtr->display();
return 0;
}
Output
Derived class function
- A virtual function display() is defined in the base class and is overridden in the derived class.
- A base class pointer is used to point to a derived class object.
- When display() is called using the base pointer, the derived class version is executed at runtime due to dynamic binding.
Runtime Vs Compiler Time Polymorphism
The major difference between the compile-time and runtime polymorphism is:
Compile Time Polymorphism | Run time Polymorphism |
|---|---|
Also called static binding | Also called dynamic binding |
Achieved using function overloading and operator overloading | Achieved using virtual functions and function overriding |
Decision made by the compiler at compile time | Decision made at runtime using vtables |
Faster due to early binding | More flexible but slightly slower |