Virtual Functions, Pure Virtual Functions, Abstract Classes & vptr/vtable
Idea in plain words
Upcasting: Base class pointer can point to a derived class object.
Exam tip
High-yield exam topic (Very High).
Explanation
Runtime polymorphism decouples system architectures from concrete implementations. The compiler achieves dynamic dispatch via vptr indexing into the class vtable at runtime (costing one pointer indirection). Virtual destructors are essential in C++ whenever a class contains any virtual functions.
Key points
- Static Binding (Compile-time): Invokes base function based on pointer type; Dynamic Binding (Runtime): Invokes derived override based on pointed-to object.
- Virtual Functions: Declared with virtual keyword in base class, resolved dynamically at runtime.
- vtable & vptr: The compiler creates a Virtual Method Table (vtable) storing function pointers for each class with virtual functions, and injects a hidden pointer (vptr) into each object instance.
- Pure Virtual Function (virtual void draw() = 0): Makes the class Abstract. Cannot instantiate abstract classes.
- Virtual Destructor: Guarantees derived class destructors run when deleting derived objects through base class pointers, preventing memory leaks.
Example
Example
Without virtual destructor: 'delete s' where s is Shape* pointing to Circle would only execute ~Shape(), leaking Circle's internal resources.
Code examples
// Runtime Polymorphism & Virtual Destructors in C++
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() const = 0; // Pure virtual function => Abstract Class
virtual double area() const = 0;
virtual ~Shape() { // Crucial Virtual Destructor!
cout << "Shape destroyed\n";
}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
void draw() const override { cout << "Drawing Circle\n"; }
double area() const override { return 3.14159 * radius * radius; }
~Circle() override { cout << "Circle destroyed\n"; }
};
int main() {
Shape *s = new Circle(5.0); // Upcasting
s->draw(); // Dynamic dispatch via vtable!
cout << "Area: " << s->area() << endl;
delete s; // Calls ~Circle() then ~Shape() safely
return 0;
}
Exam questions
- [8 Marks] Explain the internal working mechanism of virtual functions using vtable (Virtual Table) and vptr (Virtual Pointer) diagrams.
- [7 Marks] What is an abstract class and pure virtual function? Why is a virtual destructor necessary when dealing with inheritance?
- [7 Marks] Compare compile-time polymorphism with run-time polymorphism with clear code demonstrations.