Polymorphism means “many forms”, where a single function or method can behave differently in different situations. In C++, it allows the same function to produce different outputs depending on the object that calls it.
- Same function, different behavior depending on the object
- Achieved through function overloading and function overriding
- Helps improve code reusability and flexibility
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.
#include <iostream>
using namespace std;
// Base class
class Animal {
public:
// Virtual function
virtual void speak() {
cout << "Animal sound" << endl;
}
};
// Derived class Dog
class Dog : public Animal{
public:
void speak() {
cout << "Bark" << endl;
}
};
// Derived class Cat
class Cat : public Animal {
public:
void speak(){
cout << "Meow" << endl;
}
};
int main(){
// Base class pointer
Animal* a;
Dog d;
Cat c;
// Pointer refers to Dog object
a = &d;
a->speak();
// Pointer refers to Cat object
a = &c;
a->speak();
return 0;
}
Output
Bark Meow
Explanation: In this program, the speak() function behaves differently depending on the object. When the pointer refers to the Dog object, it prints Bark, and when it refers to the Cat object, it prints Meow. This demonstrates runtime polymorphism in C++.
Types of Polymorphism
In C++, polymorphism is mainly divided into two types:

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
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
Explanation: In the above example, two add() functions are defined with the same name but different parameter types: one for integers and one for floating-point numbers. The correct version of the add() function is called at compile time based on the type of arguments passed, allowing the same function name to be used for different data types.
2. Operator Overloading
Operator overloading allows operators such as +, -, and * to work with user-defined data types. 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.
#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
Explanation: Above program demonstrates operator overloading in C++ by redefining the + operator for the Complex class. The overloaded operator adds the real and imaginary parts of two complex number objects. When c1 + c2 is executed, the overloaded + operator is called automatically, and the result is stored in c3. The final output displays the sum of the two complex numbers.
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:
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.
#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
Explanation: In the above example, a virtual function display() is defined in the base class Base, and it is overridden in the derived class Derived. The Base class pointer basePtr points to an object of the Derived class. When the display() function is called using the basePtr, the derived class version of the display() function is called, and prints "Derived class function." This is possible because call is resolved at the runtime.
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 |