Polymorphism in C++ - Virtual Functions and Dynamic Binding


Polymorphism is a fundamental concept in object-oriented programming that allows objects of different classes to be treated as objects of a common base class. It enables dynamic binding and runtime method selection, leading to flexible and extensible code. In C++, polymorphism is achieved through virtual functions. In this guide, we'll explore the concept of polymorphism in C++, with a focus on virtual functions and dynamic binding, and provide sample code to illustrate its usage.


Virtual Functions

Virtual functions are member functions declared in a base class and redefined in derived classes. They allow derived classes to provide specific implementations while maintaining a common interface. Here's an example of a base class with a virtual function:


#include <iostream>
using namespace std;
class Shape {
public:
// Virtual function
virtual void displayArea() {
cout << "Shape area: Not specified" << endl;
}
};
class Circle : public Shape {
public:
Circle(double r) : radius(r) {}
void displayArea() override {
double area = 3.14159 * radius * radius;
cout << "Circle area: " << area << endl;
}
private:
double radius;
};
int main() {
Shape* shape;
Circle circle(5.0);
shape = &circle;
// Calls the derived class's implementation
shape->displayArea();
return 0;
}

In this example, the `Shape` class has a virtual function `displayArea()`, and the `Circle` class, derived from `Shape`, overrides this function to provide a specific implementation for calculating the area of a circle. When a `Circle` object is assigned to a `Shape` pointer, dynamic binding ensures that the correct implementation of `displayArea()` is called at runtime.


Dynamic Binding

Dynamic binding, also known as late binding, is the process of selecting the appropriate function implementation at runtime. This allows you to work with objects based on their actual types, not just their declared types. Here's an example demonstrating dynamic binding:


#include <iostream>
using namespace std;
class Shape {
public:
// Virtual function
virtual void displayArea() {
cout << "Shape area: Not specified" << endl;
}
};
class Circle : public Shape {
public:
Circle(double r) : radius(r) {}
void displayArea() override {
double area = 3.14159 * radius * radius;
cout << "Circle area: " << area << endl;
}
private:
double radius;
};
int main() {
Shape* shapes[2];
Circle circle(5.0);
shapes[0] = &circle;
Shape shape; // A base class object
shapes[1] = &shape;
for (int i = 0; i < 2; i++) {
shapes[i]->displayArea();
}
return 0;
}

In this example, we have an array of pointers to `Shape` objects, which includes both a `Circle` object and a base class `Shape` object. The `displayArea()` method is called on each object, and dynamic binding ensures that the correct implementation is invoked for each object, based on its actual type.


Conclusion

Polymorphism, virtual functions, and dynamic binding are essential concepts in C++ that promote flexibility and code extensibility. By allowing derived classes to provide specific implementations for virtual functions, you can write more maintainable and versatile code that can adapt to different object types at runtime.