Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
154 views
in Technique[技术] by (71.8m points)

c++ - Mixing virtual and non-virtual inheritance of a base class

This is the code:

struct Biology
{    
    Biology() { cout << "Biology CTOR" << endl; }
};

struct Human : Biology
{    
    Human() { cout << "Human CTOR" << endl; }
};

struct Animal : virtual Biology
{
    Animal() { cout << "Animal CTOR" << endl; }
};

struct Centaur : Human, Animal
{
    Centaur() { cout << "Centaur CTOR" << endl; }
};

int main()
{   
   Centaur c;

   return 0;
}

This code prints:

Biology CTOR
Biology CTOR
Human CTOR
Animal CTOR
Centaur CTOR

Why?

Since we create a Centaur object, we start from building the Centaur by constructing Human, Animal and finally Centaur (we start from the less derived to the most derived).

Let's start from Human: Human inherits from Biology, so we call Biology's constructor first. Now that Human's base class is constructed, we can finally construct the Human itself. But instead, Biology gets constructed again!

Why? What's happening behind the scenes?

Please note that it was completely intentional leaving Animal inheriting virtually from Biology and, at the same time, it was also intentional leaving Human non-virtually inheriting from Biology.

We are solving the Dreaded Diamond in an incorrect way: both Human and Animal should virtually inherit Biology to make this work.

I'm just curious.

Also, see this code:

struct Biology
{    
    Biology() { cout << "Biology CTOR" << endl; }
};

struct Human : virtual Biology
{
    Human() { cout << "Human CTOR" << endl; }
};

struct Animal : Biology
{    
    Animal() { cout << "Animal CTOR" << endl; }
};

struct Centaur : Human, Animal
{
    Centaur() { cout << "Centaur CTOR" << endl; }
};

int main()
{   
   Centaur c;

   return 0;
}

Here we have Human inheriting virtually from Biology, while Animal is set to inherit in the "classic way".

But this time, the output is different:

Biology CTOR
Human CTOR
Biology CTOR
Animal CTOR
Centaur CTOR

This because Centaur inherits at first from Human and then from Animal.

Had the order been the inverse, we'd have achieved the same result as before, in the first example - two Biology instances being constructed in a row.

What's the logic of this?

Please try to explain your way, I've already checked tons of websites speaking about this. But none seems to satisfy my request.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It's clear from the output that two Biology objects are instantiated. That is because you've made only one inheritance virtual. Two base class instances is the cause of ambiguity in dreaded diamond problem and the solution is to make (as we know) both inheritances of Biology virtual.

Recap of the hierarchy:

Biology  Biology
   |       |     # one and only one inheritance virtual
 Human   Animal
         /
    Centaur

Ok, let's read the output again with these rules in mind:

  • Base classes are constructed before derived classes.
  • Base classes are constructed in order in which they appear in the base-specifier-list.
  • Virtual base classes are constructed before non-virtual ones by the most derived class - see this.

1st output - Animal virtually inherits from Biology:

Biology CTOR     # virtual base class inherited from Animal
Biology CTOR     # non-virtual base class of Human
Human CTOR       # Human itself
Animal CTOR      # Animal's virtual base class already constructed
Centaur CTOR

2nd output - Human virtually inherits from Biology:

Biology CTOR     # virtual base class inherited from Human
Human CTOR       # Human's virtual base class already constructed
Biology CTOR     # non-virtual base class of Animal
Animal CTOR      # Animal itself
Centaur CTOR

More informative standard paragraph ([class.base.init]/10):

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).

...


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...