Virtual Function in C++: Why It Exists, With Simple Example
Without virtual, a base pointer calls the base version even for a child object. virtual makes the call decided at runtime by the actual object. Before/after proof.
First, feel the problem (without virtual)
#include <iostream>
using namespace std;
class Animal {
public:
void sound() { cout << "Some generic sound\n"; } // NOT virtual
};
class Dog : public Animal {
public:
void sound() { cout << "Bark!\n"; }
};
int main() {
Animal *a = new Dog(); // base pointer, but Dog object!
a->sound(); // which version runs?
delete a;
return 0;
}
Wrong answer came out! The object is a Dog, but "Some generic sound" printed. Why? Without virtual, the compiler decides the call by looking at the pointer's type (Animal*) at compile time. It never looks at the actual object. This is called early binding.
Now add one word — virtual
class Animal {
public:
virtual void sound() { cout << "Some generic sound\n"; } // virtual!
};
class Dog : public Animal {
public:
void sound() override { cout << "Bark!\n"; }
};
int main() {
Animal *a = new Dog();
a->sound(); // now?
delete a;
return 0;
}
Correct! The word virtual tells the compiler: "don't decide now — wait until the program runs and look at the actual object". This is late binding / runtime polymorphism. Internally, the compiler builds a hidden table of function addresses (the vtable) and each object carries a pointer to its class's table — that is how the right version is found at runtime.
Why this matters in real code
void morningRoutine(Animal *a) { // works for ANY animal -
a->sound(); // even ones written NEXT YEAR
}
Dog d; Cat c; Cow w;
morningRoutine(&d); // Bark!
morningRoutine(&c); // Meow!
morningRoutine(&w); // Moo!
One function handles every present and future animal type without modification. Every plugin system, every GUI framework, every game engine runs on exactly this mechanism.
The virtual destructor rule (senior-level question)
Animal *a = new Dog();
delete a; // if ~Animal() is NOT virtual:
// only Animal's destructor runs, Dog's part leaks!
virtual ~Animal() { }.Summary table
| Point | Normal function | Virtual function |
|---|---|---|
| Call decided | Compile time (early binding) | Runtime (late binding) |
| Decided by | Pointer/reference type | Actual object type |
| Mechanism | Direct call | vtable lookup |
| Enables | — | Runtime polymorphism |
| Cost | None | Tiny (one pointer lookup) |
पहले problem को महसूस करें (बिना virtual के)
#include <iostream>
using namespace std;
class Animal {
public:
void sound() { cout << "Some generic sound\n"; } // virtual NAHI
};
class Dog : public Animal {
public:
void sound() { cout << "Bark!\n"; }
};
int main() {
Animal *a = new Dog(); // base pointer, लेकिन Dog object!
a->sound(); // कौन-सा version चलेगा?
delete a;
return 0;
}
गलत answer निकला! Object Dog है, लेकिन "Some generic sound" print हुआ. क्यों? Virtual के बिना compiler call को compile time पर pointer के type (Animal*) से decide करता है. असली object को देखता ही नहीं. इसे early binding कहते हैं.
अब सिर्फ एक शब्द जोड़ें — virtual
class Animal {
public:
virtual void sound() { cout << "Some generic sound\n"; } // virtual!
};
class Dog : public Animal {
public:
void sound() override { cout << "Bark!\n"; }
};
int main() {
Animal *a = new Dog();
a->sound(); // अब?
delete a;
return 0;
}
सही! virtual शब्द compiler से कहता है: "अभी decide मत करो — program चलने तक रुको और असली object देखो". यही late binding / runtime polymorphism है. अंदर से compiler function addresses की एक hidden table (vtable) बनाता है और हर object अपनी class की table का pointer साथ रखता है — runtime पर सही version ऐसे मिलता है.
Real code में यह क्यों matter करता है
void morningRoutine(Animal *a) { // HAR animal के लिए चलता है -
a->sound(); // अगले साल लिखे जाने वालों के लिए भी
}
Dog d; Cat c; Cow w;
morningRoutine(&d); // Bark!
morningRoutine(&c); // Meow!
morningRoutine(&w); // Moo!
एक function हर मौजूदा और आने वाले animal type को बिना बदलाव के handle करता है. हर plugin system, हर GUI framework, हर game engine इसी mechanism पर चलता है.
Virtual destructor rule (senior-level question)
Animal *a = new Dog();
delete a; // अगर ~Animal() virtual NAHI है:
// सिर्फ Animal का destructor चलेगा, Dog का हिस्सा leak!
virtual ~Animal() { }.Summary table
| Point | Normal function | Virtual function |
|---|---|---|
| Call decide | Compile time (early binding) | Runtime (late binding) |
| Decide करता है | Pointer/reference का type | असली object का type |
| Mechanism | Direct call | vtable lookup |
| Enable करता है | — | Runtime polymorphism |
| Cost | कुछ नहीं | बहुत छोटी (एक pointer lookup) |
Frequently Asked Questions
What is a virtual function in simple words?
A virtual function tells the compiler to decide which version to call at runtime based on the actual object, not at compile time based on the pointer type — enabling runtime polymorphism.
What is a vtable?
A hidden table of virtual function addresses that the compiler creates per class; each object carries a pointer to its class vtable, which is how the correct function is found at runtime.
Why should a base class destructor be virtual?
So that deleting a derived object through a base pointer runs the derived destructor too; otherwise only the base part is destroyed and the derived part leaks resources.