Inheritance & Polymorphism

Course 1 · Ch 7
Inheritance & Polymorphism
c2-7's own hand-rolled function-pointer struct, made real by the language itself

c2-7 ended by naming this chapter directly: what a struct of function pointers, wired up by hand, actually becomes once C++ does it for you. This is that reveal, in full.

Base and Derived Classes

class Shape { public: double area() { return 0; } }; class Circle : public Shape { public: double radius; double area() { return 3.14159 * radius * radius; } };

Without virtual — The Problem

Shape *s = new Circle{5.0}; s->area(); // calls Shape::area() — NOT Circle::area(), even though s points at a Circle

A genuine, surprising gotcha: calling area() through a Shape * pointing at a Circle calls Shape's own area(), not Circle's. This is static binding — resolved at compile time, based on the pointer's declared type, not the actual object it points to.

virtual — Making Dispatch Real

class Shape { public: virtual double area() { return 0; } }; // now, with the same Shape* s pointing at a Circle: s->area(); // correctly calls Circle::area()

Adding virtual changes everything: dispatch now resolves at runtime, based on the actual object's real type. This is exactly c2-7's own hand-built mechanism, made real by the language.

The vtable, Named Directly

Under the hood, a class with virtual functions gets a hidden vtable — a table of function pointers, exactly c2-7's own hand-rolled Circle struct with its own function-pointer member. Every object of such a class carries a hidden pointer to its class's vtable. Calling a virtual function through a base pointer looks up the real function through this table, at runtime — literally the same shape->area(shape) code c2-7 wrote by hand, now generated automatically.

override and Pure Virtual Functions

override (C++11+) is a safety check confirming a derived function genuinely overrides a base virtual — catching typos or signature mismatches as compile errors instead of silently creating an unrelated function.

double area() override { return 3.14159 * radius * radius; }

= 0 marks a pure virtual function — the class becomes abstract, unable to be instantiated directly, requiring every derived class to provide its own implementation.

virtual double area() = 0; // Shape itself can no longer be instantiated
ConceptC (c2-7)C++
Dispatch tablehand-written struct of function pointersvtable — generated automatically
Wiring a function pointer correctlythe programmer's own responsibility, uncheckedthe compiler builds it, guaranteed correct
Forcing an implementation to existno mechanism — a NULL entry is UB when called= 0 (pure virtual) — a compile-time requirement
Give a virtual base class a virtual destructor
Any class with virtual functions should make its destructor virtual too — otherwise deleting a derived object through a base pointer skips the derived class's own destructor, a real and genuinely common source of leaks.
Forgetting virtual is a silent bug, not a compile error
Code compiles fine without virtual, calls the wrong function, and gives no warning by default. C, by contrast, never pretends to have automatic dispatch at all — c2-7's own struct-of-function-pointers approach fails loudly (a NULL call) rather than silently calling the wrong thing.

Coding Challenges

Challenge 1

Write a Shape base class with a non-virtual area() returning 0, and a Circle derived class overriding it. Call area() through a Shape* pointing at a Circle and report what actually gets called.

📄 View solution
Challenge 2

Add virtual to Shape::area() from Challenge 1 (and override to Circle::area()), then run the identical Shape* call again. Report how the result changed and why.

📄 View solution
Challenge 3

Explain precisely what the vtable is, and why calling a virtual function through a base-class pointer is described as "the exact code c2-7 built by hand, generated automatically" — name the specific piece c2-7 wrote manually that the vtable now replaces.

📄 View solution

Chapter 7 Quick Reference

  • class Derived : public Base { } — inherits members and methods
  • Without virtual — static binding, resolved by the pointer's declared type at compile time
  • With virtual — dynamic binding, resolved by the actual object's type at runtime
  • The vtable — a hidden, compiler-generated table of function pointers, exactly c2-7's own hand-rolled mechanism
  • override — compile-time safety check; = 0 — pure virtual, makes a class abstract
  • Always give a base class with virtual functions a virtual destructor
  • Next chapter: references vs. pointers, in depth — when to reach for each