Why a rule to explicitly call a virtual base class

2019-07-07 01:43发布

问题:

This question already has an answer here:

  • Why must virtual base classes be constructed by the most derived class? 3 answers

The following code:

#include<iostream>
using namespace std;

class Man
{
    int stories;

public:
    Man(int stories) : stories(stories) {cout << "A man is created with " << stories << " stories." << endl;};
};

class Grandpa : public virtual Man
{
    int pipes;

public:
    Grandpa(int stories, int pipes) : Man(1000), pipes(pipes)
    {
        stories += stories;
        cout << "Grandpa is created with " << stories << " stories and pipes." << endl;
    };
};

class Father : public Grandpa
{
    int cars;

public:
    Father(int stories, int cars) : Man(1000), Grandpa(1000, 1), cars(cars)
    {
        stories += stories;
        cout << "Father is created with " << stories << " stories and cars." << endl;
    };
};

class Son : public Father
{
    int girls;

public:
    Son(int stories, int girls) : Man(1000), Father(1000, 3), girls(girls)
    {
        stories += stories;
        cout << "Son is created with " << stories << " stories and girls." << endl;
    };
};

int main()
{
    Son Dick(1000, 5);
    return 0;
}

Gives the following output on the run:

A man is created with 1000 stories.

Grandpa is created with 2000 stories and pipes.

Father is created with 2000 stories and cars.

Son is created with 2000 stories and girls.

And it does not compile, when I do not call Man(int) in Father's and Son's initializers' lists. It is trying to call Man(void), and Man() is not defined. Why is it so? Yet, I think, when Father's or Son's constructors were called, the virtual base's constructor had already been called in Grandpa! Moreover thus, I would expect the output to be:

A man is created with 1000 stories.

Grandpa is created with 2000 stories and pipes.

Father is created with 3000 stories and cars.

Son is created with 4000 stories and girls.

To recapitulate then: why a virtual base class constructor has to be called explicitly in an initializer list of a derived class, although it is already placed in an younger's ancestor initializer list? Why it is seemingly not called in the younger ancestor, and a default base class is tried to be called instead?

EDIT due to finding an answer to problem with numbers What a shame :) I should have made int stories in Grandpa protected int _stories; and then the output would be as expected: 1000 stories for Man, 2000 for Grandpa, 3000 for Father and 4000 for Dick :) Otherwise stories += stories acts on local variable instead of the Man's member... Sorry for bothering!

回答1:

The call to the virtual base's constructor comes from the most-derived type of the object being created. In this case, that's Son, so Son needs to call Man's constructor.

However, it's also possible to create a Father that isn't actually a Son (e.g., main having Father Dick(1000, 5)). Because of that, Father might end up being the one to initialize Man, meaning it needs to call Man's constructor. The same is true for Grandpa.

If you make Man, Grandpa, and Father abstract classes, they can no longer possibly be the ones to initialize Man, and thus do not need a call to its constructor. However, GCC 6.1 gives me an error when I do this (Clang compiles it). I believe GCC to be wrong here.