Why does the base constructor get called instead of the one with parameters (virtual inheritance)?

Viewed 45
#include <iostream>

using namespace std;

class Point
{
    int x,y;
public:
    Point()
    {
        x=0;
        y=0;
    }
    Point(int x, int y)
    {
        this->x=x;
        this->y=y;
    }
    Point(Point &p)
    {
        x=p.x;
        y=p.x;
    }
    friend class Square;
};

class Square
{
    Point _point;
    int side;
public:
    Square() {cout<<"Square.\n";}
    Square(Point &p, int side_val): _point(p), side(side_val)
    {
        cout<<"Square constructor that should be used.\n";
    }
};

class Rectangle: public virtual Square
{
    int side2;
public:
    Rectangle() {}
    Rectangle(Point &p, int side_1, int side_2): Square(p,side_1), side2(side_2) {}
};

class Rhombus: public virtual Square
{
    Point opposite_point;
public:
    Rhombus() {cout<<"Rhombus. \n";}
    Rhombus(Point &p, Point &q, int side_1): Square(p, side_1), opposite_point(q)
    {
        cout<<"Rhombus constructor that should be used \n";
    }
};

class Paralellogram: public Rectangle, public Rhombus
{
public:
    Paralellogram(Point &p, Point &q, int side_1, int side_2): Rhombus(p,q,side_1),Rectangle(p,side_1,side_2)
    {
        cout<<"Parallelogram constructor that should be used\n";
    }
};

int main()
{
    Point down_left(3,5);
    Point up_right(2,6);
    int side_1=5;
    int side_2=7;
    Paralellogram par(down_left,up_right,side_1,side_2);
}

The output I get is:

Square
Rhombus constructor that should be used
Paralellogram constructor that should be used

And what I'm trying to do is instantiate a Paralellogram that has combined variables from a Rhombus and a Rectangle (it should have a _point, opposite_point, side, side2) and I don't want them to double up hence I'm using virtual inheritance. But the constructor of Square that I intended should be used never gets called, even once, instead the base constructor gets called.

What should I do? Give up on the virtual inheritance?

2 Answers

In virtual inheritance, virtual base is constructed according to the most derived class.

class Paralellogram: public Rectangle, public Rhombus
{
public:
    Paralellogram(Point &p, Point &q, int side_1, int side_2) :
        Square(), // You have implicitly that
        Rectangle(p,side_1,side_2),
        Rhombus(p,q,side_1)
    {
        cout<<"Parallelogram constructor that should be used\n";
    }
};

You probably want

class Paralellogram: public Rectangle, public Rhombus
{
public:
    Paralellogram(Point &p, Point &q, int side_1, int side_2) :
        Square(p, side_1),
        Rectangle(p,side_1,side_2),
        Rhombus(p,q,side_1)
    {
        cout<<"Parallelogram constructor that should be used\n";
    }
};

As explained in Understanding virtual base classes and constructor calls:

There is always just one constructor call, and always of the actual, concrete class that you instantiate. It is your responsibility to endow each derived class with a constructor which calls the base classes' constructors if and as necessary, as you did in [Rectangle and Rhombus]'s constructor[s].

Ask yourself: what if the Rectangle and Rhombus calls disagreed on how the unique (since virtual) instance of Square should be initialized ? Who should we listen to ? The answer is neither. It's always the responsibility of the current class, here Parallelogram, to initialize the virtual bases. And since here no constructor is specified, it is considered that Parallelogram calls the default constructor of Square.

Related