Advanced⏱️ 10 min📘 Topic 22 of 32

🎭 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();
Output:
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!
};
Output:
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
The ability of one interface to invoke different implementations depending on the actual type at runtime. In C++ achieved through virtual functions called via base pointers/references.
Q2.What is a virtual function?Intermediate
A member function declared with `virtual` in the base class. Calls through a base pointer/reference dispatch to the actual derived implementation at runtime (dynamic binding), not the static base version.
Q3.What is a pure virtual function?Intermediate
A virtual function declared with `= 0` — has no implementation in the base. Makes the class ABSTRACT (cannot be instantiated). Forces derived classes to provide an implementation, creating a contract/interface.
Q4.How does the vtable work?Advanced
Each polymorphic class has a hidden virtual table — an array of function pointers, one per virtual function. Each object has a hidden vptr pointing to its class's vtable. A virtual call dereferences vptr and jumps through the appropriate slot.
Q5.What is object slicing?Advanced
Assigning a derived OBJECT (by value) to a base-type variable copies only the base portion — derived fields are sliced off. Avoid by working through pointers or references, never bare values, for polymorphic types.

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