3

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!

iammilind
  • 68,093
  • 33
  • 169
  • 336
forestgril
  • 85
  • 7

1 Answers1

2

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.

chris
  • 60,560
  • 13
  • 143
  • 205
  • Thank You. I think however this is not the answer I was looking for. My question is of an academic kind. I will try to ask the same again in a comment to a duplicate claim. – forestgril Jun 23 '16 at 08:03
  • Aha, I also have to admit, I do not understand, why the numbers in the program do not increase :( When I call Man(1000) in Son(1000), I would suppose, that call of Father(1000) would add 1000 to stories, and in Father's constructor, the call to Grandpa(1000) would add 1000 stories as well. So I expected 3000 stories in Father's case and 4000 stories in Son's. But all in all each derived classes constructor prints out only 2000 stories. – forestgril Jun 23 '16 at 08:26
  • Ok, I have responded myself to that in the next edit... – forestgril Jun 23 '16 at 08:37