A virtual function is a member function declared with the virtual keyword in a base class and overridden in a derived class. It allows function calls through base class pointers or references to invoke the implementation corresponding to the actual object type at runtime.
- Forms the foundation of runtime polymorphism in C++
- Allows derived classes to customize inherited behavior
- Enables dynamic function dispatch through base class pointers and references
#include <iostream>
using namespace std;
class Shape
{
public:
// Virtual function
virtual void calculate()
{
cout << "Area of your Shape ";
}
// Virtual destructor
virtual ~Shape()
{
cout << "Shape Destructor called\n";
}
};
// Derived class: Rectangle
class Rectangle : public Shape
{
public:
int width, height, area;
void calculate() override
{
width = 5;
height = 10;
area = height * width;
cout << "Area of Rectangle: " << area << "\n";
}
~Rectangle()
{
cout << "Rectangle Destructor called\n";
}
};
// Derived class: Square
class Square : public Shape
{
public:
int side, area;
void calculate() override
{
side = 7;
area = side * side;
cout << "Area of Square: " << area << "\n";
}
~Square()
{
cout << "Square Destructor called\n";
}
};
int main()
{
Shape *S;
Rectangle r;
S = &r;
S->calculate();
Square sq;
S = &sq;
S->calculate();
return 0;
}
Output
Area of Rectangle: 50 Area of Square: 49 Square Destructor called Shape Destructor called Rectangle Destructor called Shape Destructor called
Explanation
- calculate() is declared virtual in the Shape class.
- Both Rectangle and Square provide their own implementation.
- The function call is resolved at runtime based on the actual object type.
- This behavior is known as runtime polymorphism.
Note: It is a recommended way to use override identifier to avoid mistakes while redefine virtual function inside the derived class.
Pure Virtual Function
A pure virtual function is a virtual function declared with = 0 in a base class. It is used to enforce that derived classes provide their own implementation.
- A class containing a pure virtual function becomes an abstract class and cannot be instantiated.
- Derived classes must override the pure virtual function to provide an implementation.
- A destructor can also be declared as pure virtual (= 0); it makes the class abstract but must still be defined outside the class to ensure proper object cleanup.
#include <iostream>
using namespace std;
class Base
{
public:
// Pure virtual function
virtual void display() = 0;
// Pure virtual destructor
virtual ~Base() = 0;
};
// Definition of pure virtual destructor
Base::~Base()
{
cout << "Base destructor called" << endl;
}
class Derived : public Base
{
public:
void display() override
{
cout << "Derived class display" << endl;
}
~Derived()
{
cout << "Derived destructor called" << endl;
}
};
int main()
{
Base *basePtr;
Derived derivedObj;
basePtr = &derivedObj;
basePtr->display();
return 0;
}
Output
Derived class display
Explanation
- display() is a pure virtual function, making Base an abstract class.
- Derived overrides the display() function.
- The pure virtual destructor is defined separately and ensures proper cleanup.
- Calling display() through a base class pointer invokes the derived class implementation.
Early Binding and Late Binding
Binding is the process of associating a function call with the function definition that will be executed. In C++, binding can occur at either compile time or runtime.
- Early Binding (Static Binding): The function call is resolved during compilation. Non-virtual functions use early binding, making function calls faster.
- Late Binding (Dynamic Binding): The function call is resolved at runtime based on the actual object type. Virtual functions use late binding to enable runtime polymorphism.
#include <iostream>
using namespace std;
class base
{
public:
virtual void print()
{
cout << "print base "
"class\n";
}
void show()
{
cout << "show base class\n";
}
};
class derived : public base
{
public:
void print()
{
cout << "print derived class\n";
}
void show()
{
cout << "show derived class\n";
}
};
int main()
{
base *bptr;
derived d;
bptr = &d;
// Virtual function,binded at runtime
bptr->print();
// Non-virtual function,binded at compile time
bptr->show();
return 0;
}
Output
print derived class show base class
Explanation
- print() is a virtual function, so the call is resolved at runtime and executes derived::print().
- show() is a non-virtual function, so the call is resolved at compile time and executes base::show().
- This demonstrates the difference between late binding and early binding.
Runtime Resolution Using vtable and vptr
Virtual functions achieve runtime polymorphism through two compiler-generated mechanisms: vtable and vptr.

- vtable (Virtual Table): A table maintained for each class that stores the addresses of its virtual functions.
- vptr (Virtual Pointer): A hidden pointer stored in each object that points to the corresponding class's vtable.
- In the diagram, each object contains its own vptr, which points to the appropriate vtable.
- When a virtual function is called through a base class pointer or reference, the compiler follows the object's vptr to locate the correct function address in the vtable.
- This ensures that the function belonging to the actual object type (such as Manager or Engineer) is executed at runtime.
Rules for Virtual Functions
Virtual functions follow these important rules:
- Virtual functions are defined in the base class and can be overridden in derived classes (not mandatory; base version is used if not overridden).
- They must have the same prototype in base and derived classes.
- They are used through a base class pointer or reference to achieve runtime polymorphism.
- A class may have a virtual destructor in case of dynamic memory allocation, but never a virtual constructor.
- Virtual functions cannot be static, but they can be friend functions of another class.
Limitations of Virtual Functions
While virtual functions enable runtime polymorphism, they also have some drawbacks:
- Slight Performance Overhead: Virtual function calls are resolved at runtime, making them slightly slower than normal function calls.
- Additional Memory Usage: Classes with virtual functions require a hidden vptr for runtime dispatch.
- Harder to Debug: In complex programs, it can be more difficult to determine which function implementation is actually being executed.