6

In many tutorials describing the usage of virtual base classes (usually used to solve the diamond problem), they often have a code similar to the design of this structure:

class Animal
{
public:
    Animal()
    {
        cout << "Creating Animal\n";
    }
};

///////////////////////////

class FourLegs : virtual public Animal
{
public:
    FourLegs()
    {
        cout << "Creating FourLegs\n";
    }
};

///////////////////////////

class Mammal : virtual public Animal
{
public:
    Mammal()
    {
        cout << "Creating Mammal\n";
    }
};

///////////////////////////

class Fox : public FourLegs, public Mammal
{
public:
    Fox()
    {
        cout << "Creating Fox\n";
    }
};

When I create an instance of Fox, I get the expected output, only one Animal created:

Creating Animal
Creating FourLegs
Creating Mammal
Creating Fox

As you can see, I have the two tier-2 classes inherit virtually. Now, if only one tier-2 class is inherited virtually, and the other is inherited just publicly, interesting outputs can occur. For example, if if FourLegs is inherited public and Mammal is inherited virtual public, this is the output:

Creating Animal
Creating Animal
Creating FourLegs
Creating Mammal
Creating Fox

This is strange and brings up the question: What is the complete process of creating a class that involves virtual inheritance somewhere in the inheritance tree?

On the other hand, if I FourLegs if inherited virtual public, and Mammal is inherited public, then the output is as just normal (as if nothing was inherited virtual public):

Creating Animal
Creating FourLegs
Creating Animal
Creating Mammal
Creating Fox
ZERO
  • 316
  • 7
  • 16
  • 2
    I'm sure this is just an example. However, inheriting `FourLegs` would be strange because a fox *has* four legs. – Jesse Good Jun 24 '12 at 21:34
  • Well the names of these classes are not that important. I just want to understand more about inheriting virtually. – ZERO Jun 24 '12 at 21:35
  • 2
    @JesseGood, maybe renaming `FourLegs` to `FourLegCreatures` would resolve the issue. – unkulunkulu Jun 24 '12 at 21:35
  • Now I know who to come to to ask what my classes should be named! – ZERO Jun 24 '12 at 22:19
  • @ZERO: It's not just a name, inheritance and composition are very fundamental concepts, please google `what is the difference between inheritance and composition c++` and see [this question](http://stackoverflow.com/questions/453738/inheritance-or-composition-rely-on-is-a-and-has-a) – Jesse Good Jun 24 '12 at 23:37

2 Answers2

3

Straight from the standard, 12.6.2/10 [class.base.init]:

In a non-delegating constructor, initialization proceeds in the following order:

  • First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

  • Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).

  • Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

  • Finally, the compound-statement of the constructor body is executed.

[Note:The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. —end note]

The first bullet explains how initialization is done with classes that involve virtual inheritance.

K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • So first, the compiler goes to the constructor of Fox and sees it inherits from both FourLegs and Mammal? Then, it goes to construct parent classes, starting from the first direct-parent class in it's inheritance list (where you describe how the class is inherited, etc). What does it do when it finds the class is inherited virtually? I think I've read somewhere that the compiler constructs virtually inherited classes first. If this is correct, why is this? What happens after this step? – ZERO Jun 24 '12 at 22:22
  • @ZERO: you are misinterpreting the quote. First all virtual bases are created in DFS order, then when all virtual bases have been constructed the non-virtual bases are constructed from left to right (order of appearance of the base class in the *base-specifier-list*) You might want to check this older similar question [here](http://stackoverflow.com/questions/10304300/behaviour-of-virtual-inheritance-in-this-case). – David Rodríguez - dribeas Jun 24 '12 at 23:48
  • @David Rodríguez - dribeas: Interesting. When I looked at your answer in the other question, I saw the list of creation with the depth-first left-to-right search and I think that should construct perfectly fine. Why does the order change for virtual base classes? They seem to have a priority to be constructed first – ZERO Jun 25 '12 at 20:37
  • @ZERO: Virtual bases are (potentially) *shared* by multiple subobjects in your complete type, but the constructor for each virtual base has to run only *once*, so they must be treated differently than non-virtual bases (where each subobject constructs all it's bases in order). There could be different ways of tackling this problem, but a simple one is in the standard: only the most complete type will call the constructor of the virtual bases, and it will do so in a predefined order. – David Rodríguez - dribeas Jun 25 '12 at 21:00
  • @David Rodríguez - dribeas: Good point. So after constructing the first virtual base class, the compiler goes on to another class, and realizes that that class wants to initialize another instance of that virtual base class, will it skip right over? If that is correct, that means if two classes inherit virtually from a single class (virtual base class), the first one the compiler gets to will construct that virtual base class. – ZERO Jun 25 '12 at 21:42
  • @ZERO: Not really, the order is the DFS walk, but it is always the *most-derived* type that constructs the virtual bases. Consider a diamond, with root in `A`, then `B` and `C` that inherit virtually from `A` and `D` that inherits from `B` and `C` (in that order). The order of execution of the constructors will be `ABCD`, but the parameters to the `A` constructor must be provided in `D`. Whether `B` and `C` name `A` in their initializer lists or not, those parameters will not be used while constructing a `D` object. – David Rodríguez - dribeas Jun 26 '12 at 03:23
  • As of how this is implemented (which might help understand it or not), the compiler will generate multiple versions of each defined constructor (in the Itanium ABI it will generate up to 3) to be used in different roles. One will be used when creating a full object (C1), another when constructing a base (C2) and finally an allocating constructor (C3) [I will use only the number suffix from here on]. The `_1` and `_3` flavors of constructors initialize the virtual bases (in the order defined by the standard, which is known as this is a *complete* type. These never call another `_1` or _2`... – David Rodríguez - dribeas Jun 26 '12 at 03:28
  • ... constructor, but only `_2` version of each one of the bases, so the chain of execution for an *auto* allocated object of type `D` will be `(D1) -> [ A2, B2, C2 ] D1`. Where `(D1)` is the initializer list of `D1` starting execution, and `D1` is the rest of the initializer list and the body of the constructor. That is, the calls to all other constructors come from the initializer list of `D` for the bases, and after all other constructors have completed, then `D` initializer list continues with the member attributes and the constructor body. – David Rodríguez - dribeas Jun 26 '12 at 03:33
  • @ZERO "_Why does the order change for virtual base classes?_" Because by language definition, **the constructor of the most derived object is responsible for calling virtual base class constructors**. You could invent other plausible ways (easy to understand, easy to implement) to define the responsible constructor. But in C++ it is the most derived one, so the call tree is `D(A;B;C)`, IOW the order of ctor entry is `DABC` and order of ctor exit is `ABCD` (only possible order here). – curiousguy Aug 03 '12 at 02:11
  • (...) But in a different language, your rule could be used: only for the first time for any given subobject, the base subobject constructor gets to initialise its virtual "direct base" (named in the class definition after `:`) subobjects (recursively). So the call tree would be `D(B(A);C)`, IOW the order of ctor entry would be `DBAC` (the exit order cannot change). – curiousguy Aug 03 '12 at 02:14
  • (...) Or, in yet another language, you could say that initialisation list of the derived constructors _may_ override the initialiser for any virtual base classes. Lacking a final initialiser overrider, the class definition of a concrete class would be ill-formed. (I prefer that choice, I think it has nice properties.) I believe that any solution with the guaranty that base subobject are constructed before the derived object could have been seriously considered during the design of C++. (The ink of these pages have been dry for a really long time.) – curiousguy Aug 03 '12 at 02:18
0

The unexpected output is not unexpected. It occurs because FourLegs is derived from Animal and must call the constructor of Animal. The established convention of virtual-izing all intermediate classes is required to prevent the problem. The underlying problem of your example is that the concept of FourLegs is used as an inherited trait while it should be used as a compositional trait. That is, there's a field describing the number of legs a mammal/animal has inside Mammal or Animal (depending on the specific requirements) and derived classes inherit the field.

Casey
  • 10,297
  • 11
  • 59
  • 88
  • What do you mean by FourLegs being used as an inherited trait? What's the difference between that and a compositional trait? – ZERO Jun 24 '12 at 22:09
  • @ZERO See: [Google](https://www.google.com/#hl=en&output=search&sclient=psy-ab&q=what+is+the+difference+between+inheritance+and+composition+c%2B%2B&oq=what+is+the+difference+between+inheritance+and+composition+c%2B%2B&aq=f&aqi=g-bK1&aql=&gs_l=hp.3..0i8i30.7042.7042.0.9725.1.1.0.0.0.0.1567.1567.8-1.1.0...0.0.3LZGAA1xNJc&pbx=1&bav=on.2,or.r_gc.r_pw.r_qf.,cf.osb&fp=8461e36a8b512ba6&biw=1920&bih=950) and [this question](http://stackoverflow.com/questions/453738/inheritance-or-composition-rely-on-is-a-and-has-a) – Casey Jun 25 '12 at 00:12