🪤Go deeper — read the bookC++ Gotchas & Trick Questions— runnable code & full walkthrough →

oops · high

Virtual Calls in Ctors & Dtors

A virtual call made from a constructor or destructor does not behave polymorphically. As a Derived is built, Base's constructor runs first with the vptr pointing at Base's vtable, so any virtual call resolves to Base's version; the vptr is only advanced to Derived's vtable once the Derived constructor runs. Destruction mirrors this: ~Derived runs first (vptr = Derived), then the vptr is rewound to Base before ~Base runs. The language does this so a virtual call can never reach an override whose object slice is not alive, which would touch unconstructed or destroyed members. Practical rule: never depend on virtual dispatch in ctors/dtors — it calls the currently-running class; if you need derived behavior at startup, use a two-phase init() invoked after construction.

🔑 Key line

Inside a constructor or destructor the object's dynamic type is only the class currently running, so a virtual call dispatches to that class's version — never to a more-derived override; use two-phase init instead of relying on virtual dispatch during construction.

The code

struct Base {
Base() {
log();
} // virtual call DURING construction
virtual void log() {
std::puts("Base");
}
virtual ~Base() {
log();
} // virtual call DURING destruction
};
struct Derived : Base {
void log() override {
std::puts("Derived");
}
};
Derived d; // Base() runs first -> log() prints "Base" (NOT "Derived"!)
// then d is alive: d.log() prints "Derived"
// end of scope: ~Derived() then ~Base() -> log() prints "Base"

What this lesson walks through

  1. 01During Base() the object is only a Base
  2. 02Derived() upgrades the vptr
  3. 03Fully constructed: dispatch works normally
  4. 04Destruction runs in reverse
  5. 05In ~Base() the object is a Base again
  6. 06Why the language does this
  7. 07The rule in one breath

Constructing a Derived runs Base's constructor first. At that moment the Derived part does not exist yet, so the compiler sets the vptr to Base's vtable. The virtual call log() therefore resolves to Base::log — printing "Base", not "Derived".

See it animated — step by step, at your own pace

Unlock the full interactive walkthrough of Virtual Calls in Ctors & Dtors and 100+ animated C++ interview lessons.

← Previous
Virtual Destructor
Next →
Object Slicing