Polymorphism in C++
Polymorphism is the ability of objects to take many forms. In C++, polymorphism is divided into two: compile-time polymorphism and runtime polymorphism. Compile-time includes function overloading and templates. Runtime includes virtual functions that are called based on the actual object type while the program is running.
A virtual function is a class member function declared with the virtual keyword in the base class and overridden in the derived class. The vtable (virtual table) and vptr (virtual pointer) mechanism enable dynamic dispatch of virtual function calls.
Code Example
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Shape {
protected:
string color;
public:
Shape(string c) : color(c) {}
virtual double area() {
return 0;
}
virtual string getType() {
return "Shape";
}
virtual ~Shape() {}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(string c, double w, double h) : Shape(c), width(w), height(h) {}
double area() override {
return width * height;
}
string getType() override {
return "Rectangle";
}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(string c, double r) : Shape(c), radius(r) {}
double area() override {
return 3.14159 * radius * radius;
}
string getType() override {
return "Circle";
}
};
class Triangle : public Shape {
private:
double base, height;
public:
Triangle(string c, double b, double h) : Shape(c), base(b), height(h) {}
double area() override {
return 0.5 * base * height;
}
string getType() override {
return "Triangle";
}
};
class Processor {
public:
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
};
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
void printShapeInfo(Shape* shape) {
cout << "Type: " << shape->getType() << endl;
cout << "Area: " << shape->area() << endl;
}
int main() {
cout << "=== Compile-time Polymorphism: Overloading ===" << endl;
Processor proc;
cout << "int add: " << proc.add(3, 4) << endl;
cout << "double add: " << proc.add(3.5, 4.5) << endl;
cout << "three args: " << proc.add(1, 2, 3) << endl;
cout << "\n=== Compile-time Polymorphism: Template ===" << endl;
cout << "Max int: " << maximum(10, 20) << endl;
cout << "Max double: " << maximum(3.14, 2.71) << endl;
cout << "\n=== Runtime Polymorphism: Virtual Functions ===" << endl;
Rectangle rect("Red", 5, 3);
Circle circ("Blue", 4);
Triangle tri("Green", 6, 4);
vector<Shape*> shapes;
shapes.push_back(&rect);
shapes.push_back(&circ);
shapes.push_back(&tri);
double totalArea = 0;
for (Shape* s : shapes) {
printShapeInfo(s);
totalArea += s->area();
cout << endl;
}
cout << "Total area of all shapes: " << totalArea << endl;
cout << "\n=== Polymorphism with pointers ===" << endl;
Shape* ptr = ˆ
cout << "Pointer to Shape, actual area: " << ptr->area() << endl;
cout << "Actual type: " << ptr->getType() << endl;
return 0;
}
Code Explanation
-
virtual double area()- A virtual function in the base class. Indicates that this function can be overridden in a derived class and that calls to it are dispatched dynamically. -
double area() override- Overrides the virtual function in the derived class.overrideensures it really does override the base class's function. -
virtual ~Shape()- A virtual destructor. Important so that the derived class's destructor is called when an object is deleted through a base class pointer. -
vtable- The virtual table created by the compiler, containing pointers to the overridden virtual functions. Each class with virtual functions has its own vtable. -
vptr- A hidden pointer in every object that points to its class's vtable. When a virtual function is called, the compiler looks up the function's address through the vptr and vtable. -
int add(int, int)vsdouble add(double, double)- Function overloading. Many functions with the same name but different parameters. The compiler selects one based on the argument types at compile time. -
template <typename T> T maximum(T a, T b)- A template function. The compiler creates a version of the function for each data type used. -
Shape* ptr = &circ- A base class pointer pointing to a derived class object.ptr->area()callsCircle::area()because of virtual dispatch.
Everyday Use Cases
Imagine a universal remote control. The "Power" button can turn on a TV, an AC, or a speaker. You do not need to know which device is being controlled — just press the button. Just like a virtual function, you have a pointer to the base class (the remote), but the function that gets called matches the actual object type (TV/AC/speaker).
Conclusion
Compile-time polymorphism: function overloading and templates, selected at compile time. Runtime polymorphism: virtual functions, selected while the program is running. The vtable holds pointers to virtual functions, and the vptr points to the object's vtable. Use a virtual destructor in a base class if it has virtual functions. override prevents function-name typos. final prevents further overriding.