Virtual functions are a fundamental concept in object-oriented programming that enable runtime polymorphism. They allow you to create a function in a base class that can be overridden by derived classes.
What is a Virtual Function?
A virtual function is a member function in a base class that is declared using the virtual keyword. When a derived class overrides this function, calling the function through a base class pointer or reference will execute the derived class’s version.
class Base {
public:
virtual void show() {
cout << "Base class show function" << endl;
}
};
class Derived : public Base {
public:
void show() override {
cout << "Derived class show function" << endl;
}
};
Why Do We Need Virtual Functions?
Consider the following example without using virtual functions:
#include <iostream>
using namespace std;
class Base {
public:
void show() {
cout << "Base class show function" << endl;
}
};
class Derived : public Base {
public:
void show() {
cout << "Derived class show function" << endl;
}
};
int main() {
Derived derivedObj;
Base* basePtr = &derivedObj;
// This will call Base's show() even though basePtr points to a Derived object
basePtr->show(); // Output: "Base class show function"
return 0;
}
The problem here is that the function call is bound at compile time (early binding), so the Base class’s show() function is called.
Now, let’s see what happens when we use virtual functions:
#include <iostream>
using namespace std;
class Base {
public:
virtual void show() {
cout << "Base class show function" << endl;
}
};
class Derived : public Base {
public:
void show() override {
cout << "Derived class show function" << endl;
}
};
int main() {
Derived derivedObj;
Base* basePtr = &derivedObj;
// This will call Derived's show() because the function is virtual
basePtr->show(); // Output: "Derived class show function"
return 0;
}
With the virtual keyword, the function call is bound at runtime (late binding), so the Derived class’s show() function is called.
Virtual Function Rules
- Virtual functions must be members of a class.
- They cannot be static members.
- They are accessed using pointers or references of the base class type.
- The prototype of the virtual function in the base class and all derived classes must be identical.
- A class can have virtual destructor but cannot have a virtual constructor.
- We can have a virtual main() function, but it serves no purpose because the main() function is called by the operating system, not through objects.
Virtual Function Table (vtable)
Virtual functions are implemented using a special mechanism called the virtual table or vtable.
- For each class with virtual functions, the compiler creates a vtable.
- Each object of that class contains a hidden pointer (vptr) that points to this vtable.
- When a virtual function is called through a base class pointer, the program follows these steps:
- Access the object’s vptr to find its vtable
- Look up the appropriate function pointer in the vtable
- Call the function through that pointer

How Runtime Polymorphism Works with Virtual Functions
Runtime polymorphism is achieved when:
- A class contains at least one virtual function
- We derive a class from it
- We redefine the virtual function in the derived class
- We access the function using a base class pointer or reference that points to a derived class object
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() {
cout << "Drawing a generic shape" << endl;
}
virtual double area() {
return 0;
}
};
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 length;
double width;
public:
Rectangle(double l, double w) : length(l), width(w) {}
void draw() override {
cout << "Drawing a rectangle" << endl;
}
double area() override {
return length * width;
}
};
void printShapeInfo(Shape* shape) {
shape->draw();
cout << "Area: " << shape->area() << endl;
}
int main() {
Shape* shapes[3];
shapes[0] = new Circle(5);
shapes[1] = new Rectangle(4, 6);
shapes[2] = new Shape();
for (int i = 0; i < 3; i++) {
printShapeInfo(shapes[i]);
}
// Don't forget to delete to avoid memory leaks
for (int i = 0; i < 3; i++) {
delete shapes[i];
}
return 0;
}
Output:
Drawing a circle
Area: 78.5397
Drawing a rectangle
Area: 24
Drawing a generic shape
Area: 0
Virtual Destructors
If you delete an object through a base class pointer and the base class doesn’t have a virtual destructor, only the base class destructor will be called, causing a memory leak for derived class resources.
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base constructor" << endl;
}
// Non-virtual destructor - problematic
~Base() {
cout << "Base destructor" << endl;
}
};
class Derived : public Base {
private:
int* data;
public:
Derived() {
cout << "Derived constructor" << endl;
data = new int[10];
}
~Derived() {
cout << "Derived destructor" << endl;
delete[] data; // This won't be called with a base pointer
}
};
int main() {
Base* ptr = new Derived();
delete ptr; // Only calls Base destructor!
return 0;
}
Output:
Base constructor
Derived constructor
Base destructor
The solution is to use a virtual destructor:
class Base {
public:
Base() {
cout << "Base constructor" << endl;
}
// Virtual destructor
virtual ~Base() {
cout << "Base destructor" << endl;
}
};
Output with virtual destructor:
Base constructor
Derived constructor
Derived destructor
Base destructor
The override and final Keywords (C++11)
C++11 introduced two keywords to make working with virtual functions clearer:
override: Explicitly indicates that a function should override a virtual function from the base class.final: Prevents further overriding of a virtual function.
class Base {
public:
virtual void func1() { cout << "Base::func1()" << endl; }
virtual void func2() { cout << "Base::func2()" << endl; }
virtual void func3() { cout << "Base::func3()" << endl; }
};
class Derived : public Base {
public:
// Using override makes the compiler check if this actually overrides a base method
void func1() override { cout << "Derived::func1()" << endl; }
// Using final prevents further derived classes from overriding func2
void func2() override final { cout << "Derived::func2()" << endl; }
// Error: doesn't override any base class method
// void otherFunc() override { cout << "Derived::otherFunc()" << endl; }
};
class Further : public Derived {
public:
void func1() override { cout << "Further::func1()" << endl; }
// Error: cannot override final method
// void func2() override { cout << "Further::func2()" << endl; }
};
Performance Considerations
Virtual functions have a small runtime overhead because:
- Each object with virtual functions needs to store a vptr
- Each virtual function call requires an extra indirection through the vtable
However, the flexibility and power of runtime polymorphism usually outweigh this small cost.
Best Practices for Virtual Functions
- Always make destructors virtual in base classes.
- Use the
overridekeyword when overriding virtual functions. - Consider using the
finalkeyword for performance-critical virtual functions. - Don’t call virtual functions from constructors or destructors.
- Be careful with object slicing when passing objects by value.
Common Pitfalls
- Object Slicing: When a derived class object is assigned to a base class object (not a pointer or reference), only the base part is copied, and the virtual function mechanism doesn’t work.
Base baseObj = derivedObj; // Object slicing occurs
baseObj.show(); // Always calls Base::show()
- Calling Virtual Functions from Constructors/Destructors: When a constructor of a base class is executed, the object is still considered to be of the base type, not the derived type. The same applies to destructors.
class Base {
public:
Base() {
cout << "Base constructor" << endl;
// This always calls Base::init(), never Derived::init()
init();
}
virtual void init() {
cout << "Base::init()" << endl;
}
};
class Derived : public Base {
public:
void init() override {
cout << "Derived::init()" << endl;
}
};
int main() {
Derived d; // Output: "Base constructor" followed by "Base::init()"
return 0;
}
Summary
Virtual functions are a powerful mechanism in C++ that enable runtime polymorphism. They allow you to:
- Define a common interface in a base class
- Provide specialized implementations in derived classes
- Work with objects through base class pointers or references while still executing the appropriate derived class functions
By using virtual functions, you can create more flexible and extensible code that follows the “open-closed principle” – open for extension but closed for modification.