Each class that contains methods (virtual functions) has a virtual jump table, or vtable, which is generated as part of the "lightweight" C++ execution environment. The vtable can be implemented in a number of ways, but the simplest implementation (which is often the fastest and most lightweight) contains a list of pointers to all methods of that class. Depending on the optimization policies, it may contain additional information to aid in debugging. The compiler substitutes function names with indirect (relative to the vtable list) references to method calls.
With this in mind, we can define polymorphic type explicitly as a class that contains one or more methods and, thus, requires the use of a vtable. Each instance of a polymorphic type has a typeid, which can be quite naturally implemented as the address of the vtable for the class.
A vtable cannot be built for a class unless the method definitions for all overrides are fully defined and findable by the linker.
The typeid of an object is set after the objects constructor has executed. If there are base classes, the typeid for an object may be set multiple times, after each base class initialization. We will use the classes defined in Example 23.1 to demonstrate that calling a virtual function from a constructor or destructor can have unexpected consequences.
[ . . . . ] class Base { protected: int m_X, m_Y; public: Base(); virtual ~Base(); virtual void virtualFun() const; }; class Derived : public Base { int m_Z; public: Derived(); ~Derived(); void virtualFun() const ; }; [ . . . . ] |
Example 23.2 shows what happens when a virtual function is called from a base class constructor or destructor.
#include |
In the output that follows, we see that the derived VF: does not get called from Base::Base(), because the base class initializer is inside an object that is not yet a Derived instance.
Base::Base: VF: the opposite of Acid: [4,12] Base::Base: VF: the opposite of Acid: [4,12] ~Derived() ~Base() VF: the opposite of Acid: [5,13] ~Base() VF: the opposite of Acid: [4,12]
Calling virtual methods from destructors is also not recommended. In the preceding output, we can see that the base virtualFun is always called from the base class constructors or destructor. Dynamic binding does not happen inside constructors or destructors.