5.3) Polymorphism in C++
Table of Contents
What is Polymorphism?
Polymorphism is a fundamental concept in object-oriented programming (OOP) that allows objects of different classes to be treated as objects of a common base class. It enables you to write code that can work with objects of multiple types in a unified way, providing flexibility and extensibility in your programs.
At its core, polymorphism allows you to:
- Treat objects of different derived classes as objects of a common base class.
- Call methods on these objects through pointers or references to the base class, and the appropriate method implementation is determined at runtime based on the actual object type.
There are two main types of polymorphism: compile-time polymorphism and runtime polymorphism.
1. Compile-Time Polymorphism
Also known as static polymorphism, this occurs when the method to be called is determined at compile time. It is achieved through function overloading and operator overloading. In function overloading, different functions with the same name but different parameter lists are defined, and the appropriate function is selected based on the arguments provided during the function call.
2. Runtime Polymorphism
Also known as dynamic polymorphism, this occurs when the method to be called is determined at runtime. It is achieved through function overriding and is closely related to inheritance and virtual functions. In runtime polymorphism, a base class reference or pointer can be used to refer to objects of derived classes, and the appropriate method implementation is selected based on the actual object type.
Runtime polymorphism is the more common form of polymorphism and is a central concept in OOP. It enables the creation of flexible and extensible code by allowing objects of different types to be treated uniformly through their common base class.
Here’s a high-level overview of how runtime polymorphism works:
- The base class defines a virtual function.
- The derived classes override the virtual function with their own specific implementations.
- When a base class reference or pointer is used to call the virtual function on an object, the appropriate implementation is chosen based on the actual object type.
Polymorphism plays a vital role in designing modular and maintainable software systems. It allows you to create code that is adaptable to new types of objects without requiring significant modifications to the existing codebase.
Compile-time polymorphism vs Runtime polymorphism
Here’s a comparison of compile-time polymorphism (static polymorphism) and runtime polymorphism (dynamic polymorphism):
Aspect | Compile-Time Polymorphism (Static Polymorphism) | Runtime Polymorphism (Dynamic Polymorphism) |
---|---|---|
Method Resolution | Determined at compile time | Determined at runtime |
Mechanism | Function overloading, operator overloading | Function overriding |
Binding | Early binding (static binding) | Late binding (dynamic binding) |
Object Type | Known at compile time | Known at runtime |
Use Cases | Known set of methods with different parameters | Inheritance and varying implementations |
Flexibility | Limited flexibility in method invocation | High flexibility in method invocation |
Performance | Generally faster due to early binding | Slightly slower due to late binding |
Virtual Functions | Not used in compile-time polymorphism | Core concept; requires virtual functions |
Base Class Pointer/Reference | Not commonly used | Commonly used for accessing derived class methods |
Code Size | Often results in larger code size | More efficient memory usage |
Both compile-time polymorphism and runtime polymorphism have their own advantages and use cases. Compile-time polymorphism is useful when you have a known set of methods with different parameter lists, and it’s determined at compile time which method to call based on the parameters provided. Runtime polymorphism is more flexible and dynamic, allowing you to work with objects of different types through a common interface. It’s achieved through function overriding and is particularly useful in scenarios involving inheritance and varying implementations of methods.
The choice between compile-time and runtime polymorphism depends on the specific requirements of your program and the design goals you are aiming to achieve.
Benefits of Polymorphism
- Unified interface: Different objects can be treated uniformly using the same base class interface.
- Extensibility: Adding new derived classes does not require changes to the code that uses polymorphism.
- Code reusability: Common behaviors can be defined in the base class, and derived classes can provide specific implementations.
- Flexible design: Polymorphism allows you to write more flexible and adaptable code that can handle various object types.
Example of Polymorphism in C++
Let’s delve into a detailed explanation of polymorphism in C++ with code examples to illustrate the concept.
1. Base Class and Derived Classes
We’ll start by defining a base class Shape
and two derived classes Circle
and Rectangle
.
#include <iostream>
class Shape {
public:
virtual void display() {
std::cout << "This is a shape." << std::endl;
}
};
class Circle : public Shape {
public:
void display() override {
std::cout << "This is a circle." << std::endl;
}
};
class Rectangle : public Shape {
public:
void display() override {
std::cout << "This is a rectangle." << std::endl;
}
};
2. Polymorphism in Action
We’ll use polymorphism to create an array of pointers to the base class type, and these pointers can point to objects of various derived classes.
int main() {
Shape* shapes[3];
Circle circle;
Rectangle rectangle;
shapes[0] = &circle;
shapes[1] = &rectangle;
shapes[2] = new Circle();
for (int i = 0; i < 3; ++i) {
shapes[i]->display(); // Calls the appropriate overridden function based on the object type
}
delete shapes[2]; // Deleting dynamically allocated object
return 0;
}
Output:
This is a circle.
This is a rectangle.
This is a circle.
In this example:
- The base class
Shape
has a virtual functiondisplay()
. - The derived classes
Circle
andRectangle
override thedisplay()
function. - An array of pointers to the
Shape
class is created, holding pointers to objects of different derived classes. - During the loop, the
display()
function is called on each object through the pointers, and the appropriate overridden version is executed based on the actual object type. This is known as runtime polymorphism.