Polymorphism is one of the most important concepts of Object-Oriented Programming (OOP). The word polymorphism means "many forms". In C++, polymorphism allows the same interface, function, or operation to behave differently in different situations.
Polymorphism allows one name or interface to represent different behaviors.
class Animal {
public:
virtual void sound() {
std::cout <<
"Animal sound";
}
};
class Dog : public Animal {
public:
void sound() override {
std::cout <<
"Dog barks";
}
};
The sound() function behaves differently for different
objects.
The term polymorphism comes from two words:
Therefore, polymorphism means many forms.
For example, a function named draw() may behave differently
for a circle, rectangle, and triangle.
C++ commonly uses two major types of polymorphism:
Compile-time polymorphism is resolved during compilation, while runtime polymorphism is resolved while the program is running.
Compile-time polymorphism is determined by the compiler.
Common examples include:
void show(int x) {
std::cout <<
"Integer";
}
void show(double x) {
std::cout <<
"Double";
}
The compiler selects the appropriate function based on the arguments.
Function overloading means defining multiple functions with the same name but different parameter lists.
class Calculator {
public:
int add(int a, int b) {
return a + b;
}
double add(
double a,
double b
) {
return a + b;
}
};
The compiler determines which version of add() should be
called.
void display(int x) {
std::cout << x;
}
void display(int x, int y) {
std::cout <<
x << " " << y;
}
int main() {
display(10);
display(10, 20);
return 0;
}
The number of parameters is different, so the functions can be overloaded.
Operator overloading allows operators to work with user-defined objects.
class Number {
public:
int value;
Number(int v) {
value = v;
}
Number operator+(
const Number& other
) {
return Number(
value + other.value
);
}
};
Here, the + operator is given a meaning for
Number objects.
Runtime polymorphism allows the program to select the appropriate overridden function during execution.
It is commonly implemented using:
class Animal {
public:
virtual void sound() {
std::cout <<
"Animal sound";
}
};
A virtual function is a member function declared with the
virtual keyword in a base class.
class Animal {
public:
virtual void sound() {
std::cout <<
"Animal sound";
}
};
A derived class can override the virtual function.
Function overriding occurs when a derived class provides its own implementation of a virtual function from the base class.
class Animal {
public:
virtual void sound() {
std::cout <<
"Animal sound";
}
};
class Dog : public Animal {
public:
void sound() override {
std::cout <<
"Dog barks";
}
};
The override keyword tells the compiler that a derived
class function is intended to override a virtual function.
class Dog : public Animal {
public:
void sound() override {
std::cout <<
"Dog barks";
}
};
Using override helps detect mistakes in function
signatures.
A base class pointer can point to a derived class object.
class Animal {
public:
virtual void sound() {
std::cout <<
"Animal sound";
}
};
class Dog : public Animal {
public:
void sound() override {
std::cout <<
"Dog barks";
}
};
int main() {
Dog dog;
Animal* animal = &dog;
animal->sound();
return 0;
}
Because sound() is virtual, the derived implementation is
selected.
A base class reference can also refer to a derived object.
Dog dog;
Animal& animal = dog;
animal.sound();
If sound() is virtual, the derived implementation is
called.
Without a virtual function, a call through a base pointer or reference can resolve to the base-class version rather than the overridden derived version.
class Animal {
public:
virtual void sound() {
std::cout <<
"Animal sound";
}
};
The virtual keyword enables dynamic dispatch for the
function.
Dynamic dispatch means that the function implementation is selected according to the actual object involved in the call.
class Animal {
public:
virtual void sound() {
std::cout <<
"Animal";
}
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void sound() override {
std::cout <<
"Dog";
}
};
Animal* a = new Dog();
a->sound();
delete a;
The call to sound() uses the Dog
implementation.
A pure virtual function is declared by assigning 0 to the
function declaration.
class Shape {
public:
virtual double area() = 0;
};
A class containing a pure virtual function is an abstract class.
An abstract class is a class that cannot normally be instantiated directly. It is often used as a common interface for derived classes.
class Shape {
public:
virtual double area() = 0;
};
class Circle : public Shape {
public:
double area() override {
return 3.14 * 5 * 5;
}
};
Objects of Circle can be created, but a direct
Shape object cannot be created.
#include <iostream>
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
public:
void draw() override {
std::cout <<
"Drawing Circle";
}
};
class Rectangle : public Shape {
public:
void draw() override {
std::cout <<
"Drawing Rectangle";
}
};
int main() {
Circle circle;
Rectangle rectangle;
Shape* s1 = &circle;
Shape* s2 = &rectangle;
s1->draw();
s2->draw();
return 0;
}
class Animal {
public:
virtual void sound() = 0;
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void sound() override {
std::cout <<
"Dog barks";
}
};
class Cat : public Animal {
public:
void sound() override {
std::cout <<
"Cat meows";
}
};
The same sound() interface can represent different
behaviors.
void makeSound(Animal& animal) {
animal.sound();
}
int main() {
Dog dog;
Cat cat;
makeSound(dog);
makeSound(cat);
return 0;
}
The function accepts an Animal reference but can work with
different derived objects.
void showSound(Animal* animal) {
animal->sound();
}
int main() {
Dog dog;
Cat cat;
showSound(&dog);
showSound(&cat);
return 0;
}
A base pointer can provide a common interface for different derived objects.
When a class is intended to be used polymorphically, its destructor is often declared virtual.
class Animal {
public:
virtual ~Animal() = default;
virtual void sound() = 0;
};
This helps ensure that deleting a derived object through a base-class pointer performs the appropriate destruction sequence.
| Feature | Compile-Time | Runtime |
|---|---|---|
| Decision | During compilation | During execution |
| Common Example | Function overloading | Virtual functions |
| Inheritance Required | Not necessarily | Usually involved |
| Binding | Early binding | Dynamic binding |
Inheritance provides a relationship between base and derived classes. Polymorphism allows a common interface to work with different derived objects.
class Vehicle {
public:
virtual void start() {
std::cout <<
"Vehicle starts";
}
virtual ~Vehicle() = default;
};
class Car : public Vehicle {
public:
void start() override {
std::cout <<
"Car starts";
}
};
Here, inheritance and virtual functions work together to provide runtime polymorphism.
class Payment {
public:
virtual void pay() = 0;
virtual ~Payment() = default;
};
class CashPayment : public Payment {
public:
void pay() override {
std::cout <<
"Payment by Cash";
}
};
class CardPayment : public Payment {
public:
void pay() override {
std::cout <<
"Payment by Card";
}
};
Different payment classes can provide different implementations of the
same pay() interface.
virtual keyword when runtime polymorphism is intended.override keyword.virtual functions for runtime polymorphic behavior.override in derived classes when overriding virtual functions.pay().draw() or area().start().sound().send().| Concept | Meaning |
|---|---|
| Polymorphism | One interface or name can have many forms. |
| Compile-Time Polymorphism | Behavior is selected during compilation. |
| Function Overloading | Same function name with different parameter lists. |
| Operator Overloading | Giving operators behavior for user-defined types. |
| Runtime Polymorphism | Behavior is selected during execution. |
| Virtual Function | Enables dynamic dispatch through a base interface. |
| Override | Provides a derived implementation of a virtual function. |
| Pure Virtual Function | A virtual function declared with = 0. |
| Abstract Class | A class that cannot be instantiated directly and can define a common interface. |
class Animal {
public:
virtual void sound() = 0;
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void sound() override {
std::cout <<
"Dog barks";
}
};
class Cat : public Animal {
public:
void sound() override {
std::cout <<
"Cat meows";
}
};
The same sound() interface can produce different
behaviors for different derived classes. This is the core idea of
runtime polymorphism.
override keyword helps verify overriding.Question: What is the main idea of polymorphism in C++?