I am trying to construct Derived3
in such a way that the non-default constructors are called when d2
is initialized. I would have expected that when d2
was initialized, none of the default constructors would be called. With this code:
#include <string>
#include <iostream>
struct Base
{
Base() : _message("Value initialized by default constructor")
{
std::cout << "Base default constructor called" << std::endl;
}
Base(std::string message) : _message(message)
{
}
std::string _message;
};
struct Derived1 : virtual public Base
{
Derived1() : Base()
{
std::cout << "Derived1 default constructor called" << std::endl;
}
Derived1(std::string message) : Base(message)
{
}
};
struct Derived2 : virtual public Base
{
Derived2() : Base()
{
std::cout << "Derived2 default constructor called" << std::endl;
}
Derived2(std::string message) : Base(message)
{
}
};
struct Derived3 : virtual public Derived1, virtual public Derived2
{
Derived3() : Derived1(), Derived2()
{
std::cout << "Derived3 default constructor called" << std::endl;
}
Derived3(std::string message) : Derived1(message), Derived2(message)
{
}
};
int main()
{
Derived3 d1 = Derived3();
std::cout << d1._message << std::endl; // You get what you expect.
Derived3 d2 = Derived3("Not initialized by default constructor");
std::cout << d2._message << std::endl; // You get what you do not expect.
}
I would have expected that d2._message
would be "Not initialized by default constructor"
, when in actuality it is "Value initialized by default constructor"
. Full output is:
Base default constructor called
Derived1 default constructor called
Derived2 default constructor called
Derived3 default constructor called
Value initialized by default constructor
Base default constructor called
Value initialized by default constructor
Expected Output:
Base default constructor called
Derived1 default constructor called
Derived2 default constructor called
Derived3 default constructor called
Value initialized by default constructor
Not initialized by default constructor
Why is this happening and how do I attain the expected behavior?
When you virtually inherit a base class, in all cases the virtually-inherited base class can be thought of as being always the immediate superclass of the so-called "most-derived" class. Change your Derived3
constructor as follows:
Derived3(std::string message) : Derived1(message), Derived2(message),
Base(message)
Base
is really also the base class of your Derived3
, because it gets virtually inherited from Derived1
(and Derived2
). That's what virtual inheritance is.
If you don't want Base
to be default-constructed, you have to invoke the appropriate constructor yourself, here.
Even though you don't explicitly declare Derived3
as inheriting from Base
, it inherits it virtually, hence you can invoke its constructor from Derived3
.
Note that if you declare Derived4
as a subclass of Derived3
, this constructor of Base
here won't get called. Derived4
will virtually inherit Base
, and it will be responsible for constructing it.
What's really happening when you have virtually-inherited classes, is that every constructor you declare can be thought of as actually resulting in two actual constructors, in reality: a constructor that's responsible for constructing all virtually-inherited classes, and a constructor that does not. This Derived3
constructor you declared above: you actually end up with two constructors from this. Two for the price of one: one that will construct Base
, and one that will not. The one that will construct Base
gets used when Derived3
is constructed directly, and is the most-derived class. The second constructor is the same, except that it will not construct Base
, and it gets used if a subclass of Derived3
gets instantiated. You see it as one constructor, but the compiler does a lot more work, creates two of them, and makes sure that the right one gets used, when something needs to be constructed.