C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 23:
[23.5] When my base class's constructor calls a virtual function on its this object, why doesn't my derived class's override of that virtual function get invoked?

Because that would be very dangerous, and C++ is protecting you from that danger.

The rest of this FAQ gives a rationale for why C++ needs to protect you from that danger, but before we start that, be advised that you can get the effect as if dynamic binding worked on the this object even during a constructor via The Dynamic Binding During Initialization Idiom.

First, here is an example to explain exactly what C++ actually does:

#include <iostream>
#include <string>

void println(std::string const& msg)
{ std::cout << msg << '\n'; }

class Base {
public:
  Base()              { println("Base::Base()");  virt(); }
  virtual void virt() { println("Base::virt()"); }
};

class Derived : public Base {
public:
  Derived()           { println("Derived::Derived()");  virt(); }
  virtual void virt() { println("Derived::virt()"); }
};

int main()
{
  Derived d;
  ...
}
The output from the above program will be:
Base::Base()
Base::virt() //  Not Derived::virt()
Derived::Derived()
Derived::virt()
The rest of this FAQ describes why C++ does the above. If you're happy merely knowing what C++ does without knowing why, feel free to skip this stuff.

The explanation for this behavior comes from combining two facts:

  1. When you create a Derived object, it first calls Base's constructor. That's why it prints Base::Base() before Derived::Derived().
  2. While executing Base::Base(), the this object is not yet of type Derived; its type is still merely Base. That's why the call to virtual function virt() within Base::Base() binds to Base::virt() even though an override exists in Derived.

Now some of you are still curious, saying to yourself, "Hmmmm, but I still wonder why the this object is merely of type Base during Base::Base()." If that's you, the answer is that C++ is protecting you from serious and subtle bugs. In particular, if the above rule were different, you could easily use objects before they were initialized, and that would cause no end of grief and havoc.

Here's how: imagine for the moment that calling this->virt() within Base::Base() ended up invoking the override Derived::virt(). Overrides can (and often do!) access non-static data members declared in the Derived class. But since the non-static data members declared in Derived are not initialized during the call to virt(), any use of them within Derived::virt() would be a "use before initialized" error. Bang, you're dead.

So fortunately the C++ language doesn't let this happen: it makes sure any call to this->virt() that occurs while control is flowing through Base's constructor will end up invoking Base::virt(), not the override Derived::virt().