In a virtual derivation, the virtual base is initialized by the most derived constructor. In our example, when we create a Panda
object, the Panda
constructor alone controls how the ZooAnimal
base class is initialized.
To understand this rule, consider what would happen if normal initialization rules applied. In that case, a virtual base class might be initialized more than once. It would be initialized along each inheritance path that contains that virtual base. In our ZooAnimal
example, if normal initialization rules applied, both Bear
and Raccoon
would initialize the ZooAnimal
part of a Panda
object.
Of course, each class in the hierarchy might at some point be the “most derived” object. As long as we can create independent objects of a type derived from a virtual base, the constructors in that class must initialize its virtual base. For example, in our hierarchy, when a Bear
(or a Raccoon
) object is created, there is no further derived type involved. In this case, the Bear
(or Raccoon
) constructors directly initialize their ZooAnimal
base as usual:
Bear::Bear(std::string name, bool onExhibit):
ZooAnimal(name, onExhibit, "Bear") { }
Raccoon::Raccoon(std::string name, bool onExhibit)
: ZooAnimal(name, onExhibit, "Raccoon") { }
When a Panda
is created, it is the most derived type and controls initialization of the shared ZooAnimal
base. Even though ZooAnimal
is not a direct base of Panda
, the Panda
constructor initializes ZooAnimal
:
Panda::Panda(std::string name, bool onExhibit)
: ZooAnimal(name, onExhibit, "Panda"),
Bear(name, onExhibit),
Raccoon(name, onExhibit),
Endangered(Endangered::critical),
sleeping_flag(false) { }
The construction order for an object with a virtual base is slightly modified from the normal order: The virtual base subparts of the object are initialized first, using initializers provided in the constructor for the most derived class. Once the virtual base subparts of the object are constructed, the direct base subparts are constructed in the order in which they appear in the derivation list.
For example, when a Panda
object is created:
• The (virtual base class) ZooAnimal
part is constructed first, using the initializers specified in the Panda
constructor initializer list.
• The Bear
part is constructed next.
• The Raccoon
part is constructed next.
• The third direct base, Endangered
, is constructed next.
• Finally, the Panda
part is constructed.
If the Panda
constructor does not explicitly initialize the ZooAnimal
base class, then the ZooAnimal
default constructor is used. If ZooAnimal
doesn’t have a default constructor, then the code is in error.
Virtual base classes are always constructed prior to nonvirtual base classes regardless of where they appear in the inheritance hierarchy.
A class can have more than one virtual base class. In that case, the virtual subobjects are constructed in left-to-right order as they appear in the derivation list. For example, in the following whimsical TeddyBear
derivation, there are two virtual base classes: ToyAnimal
, a direct virtual base, and ZooAnimal
, which is a virtual base class of Bear
:
class Character { /* ... */ };
class BookCharacter : public Character { /* ... */ };
class ToyAnimal { /* ... */ };
class TeddyBear : public BookCharacter,
public Bear, public virtual ToyAnimal
{ /* ... */ };
The direct base classes are examined in declaration order to determine whether there are any virtual base classes. If so, the virtual bases are constructed first, followed by the nonvirtual base-class constructors in declaration order. Thus, to create a TeddyBear
, the constructors are invoked in the following order:
ZooAnimal(); // Bear's virtual base class
ToyAnimal(); // direct virtual base class
Character(); // indirect base class of first nonvirtual base class
BookCharacter(); // first direct nonvirtual base class
Bear(); // second direct nonvirtual base class
TeddyBear(); // most derived class
The same order is used in the synthesized copy and move constructors, and members are assigned in this order in the synthesized assignment operators. As usual, an object is destroyed in reverse order from which it was constructed. The TeddyBear
part will be destroyed first and the ZooAnimal
part last.
Exercise 18.29: Given the following class hierarchy:
class Class { ... };
class Base : public Class { ... };
class D1 : virtual public Base { ... };
class D2 : virtual public Base { ... };
class MI : public D1, public D2 { ... };
class Final : public MI, public Class { ... };
(a) In what order are constructors and destructors run on a Final
object?
(b) A Final
object has how many Base
parts? How many Class
parts?
(c) Which of the following assignments is a compile-time error?
Base *pb; Class *pc; MI *pmi; D2 *pd2;
(a) pb = new Class;
(b) pc = new Final;
(c) pmi = pb;
(d) pd2 = pmi;
Exercise 18.30: Define a default constructor, a copy constructor, and a constructor that has an int
parameter in Base
. Define the same three constructors in each derived class. Each constructor should use its argument to initialize its Base
part.