Reputation: 1498
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.
Upvotes: 28
Views: 4989
Reputation: 19607
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:
1st output - Animal
virtual
ly 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
virtual
ly 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).
...
Upvotes: 44
Reputation: 30718
Biology
share one instance of Biology
base between them.Biology
have one instance each of Biology
.You have one base in each category, therefore you have one instance of Biology
brought in by Human
(and in principle shared with others) and one instance brought in by Animal
(never shared with any other base class).
Upvotes: 1
Reputation: 8270
Non virtual inheritance is an exclusive relationship, like membership. A class can be the non-virtual base class of one other class in a given complete object.
This implies that a class can override virtual functions of a non virtual base class without causing conflicts or issues.
A constructor can also initialize non virtual bases reliably.
Only virtual bases can be direct base classes of many indirect bases of a complete object. Because a virtual base class can be shared, overriders can conflict.
A constructor can try to initialize a virtual base subobject in the ctor-init-list, but if the class is further derived, that part of the ctor-init-list will be ignored.
Upvotes: 2