Skip to content

Virtual Functions / VTable ​

A virtual function declared in the base class allows runtime polymorphism — the correct function is called based on the actual object type, not the pointer type.

Problem Without Virtual ​

cpp
class Animal { public: void sound() { cout << "Animal sound" << endl; } };
class Dog : public Animal { public: void sound() { cout << "Dog barks" << endl; } };

Animal* a = new Dog();
a->sound(); // Animal sound — WRONG!
// Without virtual, C++ uses static binding — decides at compile time based
// on pointer type (Animal), not actual object (Dog)

Solution — Virtual Function ​

cpp
class Animal {
public:
    virtual void sound() { cout << "Animal sound" << endl; }
};

class Dog : public Animal {
public:
    void sound() override { cout << "Dog barks" << endl; }
};

Animal* a = new Dog();
a->sound(); // Dog barks — CORRECT!
// With virtual, C++ uses dynamic binding — decides at runtime

How VTable Works Internally ​

Step 1 — Compiler creates VTable for each class:

Animal VTable: ── Animal::sound() → 0x100
Dog VTable:    ── Dog::sound()    → 0x200

Step 2 — Each object gets a hidden VPtr (Virtual Pointer):

Animal object:                  Dog object:
├── vptr ──► Animal VTable      ├── vptr ──► Dog VTable
└── (other data)                └── (other data)

Step 3 — At runtime:

Animal* a = new Dog();
a->sound()
  → follow vptr of actual object (Dog)
  → look up sound() in Dog's VTable
  → call Dog::sound()  CORRECT

Pure Virtual Function — Abstract Class ​

cpp
class Shape {
public:
    virtual double area() = 0; // pure virtual — no implementation
}; // Shape is now Abstract Class

class Circle : public Shape {
    double r;
public:
    Circle(double r) : r(r) {}
    double area() override { return 3.14 * r * r; } // must implement
};

Virtual Destructor — Important! ​

cpp
// Without virtual destructor:
Animal* a = new Dog();
delete a; // only Animal destructor called — Dog leaks memory!

// Fix:
class Animal {
public:
    virtual ~Animal() { /* ... */ } // virtual destructor
};
// Now both Dog and Animal destructors called correctly

Rule: Always make the base class destructor virtual when using inheritance with pointers.

Override vs Hide ​

cpp
class A {
public:
    virtual void show() { /* ... */ } // virtual
    void print() { /* ... */ }         // non-virtual
};

class B : public A {
public:
    void show() override { /* ... */ } // override — runtime resolution
    void print() { /* ... */ }          // hides A::print — compile time
};

A* ptr = new B();
ptr->show();  // B — virtual, runtime
ptr->print(); // A — non-virtual, compile time

final Keyword — Prevent Overriding ​

cpp
class B : public A {
public:
    void show() final { /* ... */ } // no further overriding allowed
};

class C : public B {
    void show() override { } // ERROR — B::show is final
};

Summary Table ​

ConceptKeywordResolved at
Normal functionnoneCompile time
Virtual functionvirtualRuntime
Pure virtual= 0Runtime (must override)
Prevent overridefinalCompile time check
Force overrideoverrideCompile time check

VTable Key Facts ​

FactDetail
Created byCompiler automatically
One perClass (not per object)
VPtr perEach object
Size impactAdds one pointer per object
When createdAt least one virtual function exists

One-liner: "Virtual functions enable runtime polymorphism via VTable — each class with virtual functions gets a VTable, each object gets a VPtr, and at runtime the correct function is looked up through the VPtr."