Appearance
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 runtimeHow VTable Works Internally
Step 1 — Compiler creates VTable for each class:
Animal VTable: ── Animal::sound() → 0x100
Dog VTable: ── Dog::sound() → 0x200Step 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() CORRECTPure 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 correctlyRule: 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 timefinal 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
| Concept | Keyword | Resolved at |
|---|---|---|
| Normal function | none | Compile time |
| Virtual function | virtual | Runtime |
| Pure virtual | = 0 | Runtime (must override) |
| Prevent override | final | Compile time check |
| Force override | override | Compile time check |
VTable Key Facts
| Fact | Detail |
|---|---|
| Created by | Compiler automatically |
| One per | Class (not per object) |
| VPtr per | Each object |
| Size impact | Adds one pointer per object |
| When created | At 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."