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
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.
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.
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.