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