🎭 Polymorphism and Virtual Functions in C++ — Override, Abstract Classes
Master C++ polymorphism — virtual functions, override, pure virtual / abstract classes, the vtable mechanism, and when to use polymorphism vs templates.
Polymorphism = one interface, many implementations. C++ supports two forms:
- Compile-time — function overloading, templates
- Runtime — virtual functions (the OOP kind)
📜 Virtual functions
Declare a method virtual in the base; derived classes override it. Calls through a base pointer/reference dispatch to the actual derived version at runtime.
class Shape {
public:
virtual double area() const = 0; // pure virtual
virtual ~Shape() = default;
};
class Circle : public Shape {
double r;
public:
Circle(double rr) : r(rr) {}
double area() const override { return 3.14159 * r * r; }
};
class Square : public Shape {
double s;
public:
Square(double ss) : s(ss) {}
double area() const override { return s * s; }
};
std::vector<Shape*> shapes = { new Circle(2), new Square(3) };
for (auto* s : shapes) std::cout << s->area() << '\n';🧪 Abstract classes
A class with at least one pure virtual function (= 0) cannot be instantiated — only inherited. Acts as a contract / interface.
⚙️ The vtable
For each polymorphic class, the compiler builds a virtual table (vtable) — an array of function pointers. Each object has a hidden pointer to its class's vtable. Calling a virtual function = vtable lookup + indirect call. Slight overhead vs a direct call.
💡 Always mark overrides with `override`
The compiler verifies you're really overriding — catches typos and signature mismatches.
💻 Code Examples
Runtime dispatch through base pointer
class Animal {
public:
virtual void speak() const = 0;
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void speak() const override { std::cout << "Woof!\n"; }
};
class Cat : public Animal {
public:
void speak() const override { std::cout << "Meow!\n"; }
};
std::unique_ptr<Animal> pets[] = { std::make_unique<Dog>(), std::make_unique<Cat>() };
for (auto& p : pets) p->speak();Woof! Meow!
Forgetting override — silent bug
class Base { virtual void run(int) {} };
class Derived : public Base {
void run(double) {} // ❌ wrong signature — NEW function, not an override
};
// With override keyword:
class Derived2 : public Base {
void run(double) override; // compile error — caught!
};Always write override.
⚠️ Common Mistakes
- No virtual destructor in a polymorphic base class — leaks when deleting via base pointer.
- Slicing — assigning a derived OBJECT (by value) to a base VARIABLE drops the derived parts.
- Forgetting `override` — small signature typos create a new function instead of overriding.
- Calling a virtual function from a constructor or destructor — dispatches to the BASE version, not the derived (because the derived part isn't built yet / already destroyed).
🎯 Interview Questions
Real questions asked at top product and service-based companies.
Q1.What is polymorphism?Beginner
Q2.What is a virtual function?Intermediate
Q3.What is a pure virtual function?Intermediate
Q4.How does the vtable work?Advanced
Q5.What is object slicing?Advanced
🧠 Quick Summary
- Polymorphism = one interface, many implementations.
- Mark base methods virtual; derived methods override.
- Pure virtual (= 0) makes the class abstract / interface.
- Always declare a virtual destructor in polymorphic bases.
- Use base pointers/references to avoid slicing.
- Vtable mechanism enables runtime dispatch.