Pregunta

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
¿Fue útil?

Solución

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.

Otros consejos

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top