2
#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?

Dudu Dudu
  • 167
  • 5

2 Answers2

3

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";
    }
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 1
    Beat me to it! Yes, that's [exactly what he wants](https://wandbox.org/permlink/hggT39jfA5ViPVNW). PS: you have your initialisers in the wrong order (as does the OP). – Paul Sanders Apr 05 '22 at 15:05
  • 1
    @PaulSanders: Order fixed. – Jarod42 Apr 05 '22 at 15:10
  • 1
    Also, I just wanted to say OP, congratulations for posting such a well written question. You've made it very clear what you're asking about with a model [mre]. I wish there were more like you on this site. – Paul Sanders Apr 05 '22 at 15:17
  • @PaulSanders I appreciate it, hahah. – Dudu Dudu Apr 05 '22 at 15:23
2

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.

Julien BERNARD
  • 653
  • 6
  • 17
  • Thank you!! And following that logic (hopefully, lol), once the Square constructor with parameters is called in the Parallelogram constructor, it won't be called again in the Rhombus constructor or Rectangle constructor called by the Paralellogram constructor right? – Dudu Dudu Apr 05 '22 at 15:12
  • 1
    Yes, it won't be called again. – Julien BERNARD Apr 05 '22 at 15:15