C++ Polymorphism
Understanding compile-time and runtime polymorphism
🔄 What is Polymorphism?
Polymorphism allows objects of different types to be treated as objects of a common base type. It enables one interface to represent different underlying forms.
// Simple polymorphism example
class Animal {
public:
virtual void sound() { cout << "Some sound"; }
};
class Dog : public Animal {
public:
void sound() override { cout << "Woof!"; }
};
Output:
Woof!
Types of Polymorphism
Compile-time
Function and operator overloading
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
Runtime
Virtual functions and inheritance
virtual void display() = 0; // Pure virtual
virtual void show() { } // Virtual function
Method Overriding
Redefining base class methods
class Base { virtual void func(); };
class Derived { void func() override; };
Dynamic Binding
Method resolution at runtime
Base* ptr = new Derived();
ptr->func(); // Calls Derived::func()
🔹 Runtime Polymorphism Example
Runtime polymorphism in C++ is achieved through virtual functions and base class pointers or
references. By declaring a function as virtual in a base class like Shape,
derived classes such as Circle and Rectangle can override it. When you call
draw() or area() on a Shape*, the correct derived class method executes based
on the actual object type at runtime. This allows a single interface, like a collection of shapes, to manage diverse
objects, calculating areas like 78.5397 for circles and 24 for rectangles dynamically.
class Shape {
public:
virtual void draw() {
cout << "Drawing a shape" << endl;
}
virtual double area() = 0; // Pure virtual
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
void draw() override {
cout << "Drawing a circle" << endl;
}
double area() override {
return 3.14159 * radius * radius;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
void draw() override {
cout << "Drawing a rectangle" << endl;
}
double area() override {
return width * height;
}
};
int main() {
Shape* shapes[] = {
new Circle(5),
new Rectangle(4, 6)
};
for(int i = 0; i < 2; i++) {
shapes[i]->draw();
cout << "Area: " << shapes[i]->area() << endl;
}
return 0;
}
Output:
Drawing a circle
Area: 78.5397
Drawing a rectangle
Area: 24
🔹 Compile-time Polymorphism
Compile-time polymorphism, also known as static polymorphism, is implemented via function overloading and
templates. The compiler resolves which function to call based on the number and types of arguments at
compile time. For example, you can have multiple add() functions handling integers, doubles, or custom
types, returning results like 15 or 24. This technique improves performance by avoiding
runtime dispatch and increases code clarity by providing intuitive, type-specific implementations. It is
foundational for creating flexible APIs that work seamlessly with various data types.
class Calculator {
public:
// Function overloading
int multiply(int a, int b) {
return a * b;
}
double multiply(double a, double b) {
return a * b;
}
int multiply(int a, int b, int c) {
return a * b * c;
}
};
int main() {
Calculator calc;
cout << calc.multiply(5, 3) << endl; // Calls int version
cout << calc.multiply(2.5, 4.0) << endl; // Calls double version
cout << calc.multiply(2, 3, 4) << endl; // Calls 3-parameter version
return 0;
}
Output:
15
10
24