The virtual function is one of the core pillars of C++ object-oriented programming. It unlocks polymorphism, allowing different objects to respond to the same interface in different ways.
1. ๐ What Are Virtual Functions? #
A virtual function is a function in a base class that can be overridden in derived classes. When called through a base pointer or reference, the function invoked depends on the actual object typeโnot the pointer type.
Example:
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() {
cout << "Drawing a shape" << endl;
}
};
class Circle : public Shape {
public:
void draw() override {
cout << "Drawing a circle" << endl;
}
};
class Square : public Shape {
public:
void draw() override {
cout << "Drawing a square" << endl;
}
};
int main() {
Circle circle;
Square square;
Shape* shape1 = &circle;
Shape* shape2 = □
shape1->draw(); // Drawing a circle
shape2->draw(); // Drawing a square
}
Even though shape1 and shape2 are Shape*, C++ dispatches the correct function at runtime.
2. ๐ Key Characteristics of Virtual Functions #
2.1 Runtime (Dynamic) Binding #
Virtual functions support dynamic binding, meaning:
- The function call is resolved at runtime
- Behavior depends on the actual object type
This gives C++ strong polymorphic capabilities.
2.2 Virtual Table (vtable) #
Under the hood, virtual functions are implemented using a vtable:
- Each class with virtual functions has its own vtable.
- Each object stores a hidden pointer (
vptr) to its classโs vtable. - Virtual calls use the vptr โ vtable โ correct function.
This design ensures fast and predictable dispatch.
3. ๐ฏ When to Use Virtual Functions #
3.1 With Inheritance #
Use virtual functions to express shared behavior at the base level but different implementations at the derived level.
class Animal {
public:
virtual void makeSound() {
cout << "Generic animal sound" << endl;
}
};
class Dog : public Animal {
public:
void makeSound() override {
cout << "Woof!" << endl;
}
};
3.2 When Polymorphism Is Needed #
Virtual functions are the mechanism that enables:
- Plugin systems
- Strategy patterns
- Interfaces/abstract classes
- Heterogeneous collections
4. ๐ ๏ธ How to Use Virtual Functions #
4.1 Declaring Virtual Functions #
class Base {
public:
virtual void show();
virtual void display() {
cout << "Base class display function" << endl;
}
};
4.2 Pure Virtual Functions and Abstract Classes #
A pure virtual function forces derived classes to implement it:
class AbstractBase {
public:
virtual void run() = 0; // Pure virtual โ abstract class
};
Any class containing at least one pure virtual function becomes abstract and cannot be instantiated.
5. ๐งโ๐ป Practical Example: Plugin System #
A plugin architecture is a common real-world use case for polymorphism.
#include <iostream>
using namespace std;
class Plugin {
public:
virtual void apply() {
cout << "Applying a generic plugin" << endl;
}
};
class FilterPlugin : public Plugin {
public:
void apply() override {
cout << "Applying a filter plugin" << endl;
}
};
class DrawingPlugin : public Plugin {
public:
void apply() override {
cout << "Applying a drawing plugin" << endl;
}
};
Using polymorphism:
int main() {
FilterPlugin filter;
DrawingPlugin drawing;
Plugin* p1 = &filter;
Plugin* p2 = &drawing;
p1->apply(); // Applying a filter plugin
p2->apply(); // Applying a drawing plugin
}
This allows new plugin types to be added easilyโjust subclass Plugin and override apply().
๐ Summary #
Virtual functions are essential for writing modular, extensible, object-oriented C++:
- They enable runtime polymorphism
- Implemented through vtables
- Support both normal and pure virtual functions
- Widely used in plugin systems, frameworks, and architecture design
Mastering virtual functions is key to writing flexible, maintainable C++ code.