如何防止在基类初始化器中调用默认构造函数?

How do I keep from calling default constructor in base class intializers?

本文关键字:调用 默认 构造函数 何防止 基类 初始化      更新时间:2023-10-16

我试图以这样一种方式构造Derived3,即在初始化d2时调用非默认构造函数。我本以为在初始化d2时,不会调用任何默认构造函数。使用此代码:

#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.
}

我本以为d2._message"Not initialized by default constructor",而实际上它是"Value initialized by default constructor".完整输出为:

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

预期输出:

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

为什么会发生这种情况,我如何获得预期的行为?

当您虚拟继承基类时,在所有情况下,虚拟继承的基类始终被认为是所谓的"最派生"类的直接超类。 更改Derived3构造函数,如下所示:

Derived3(std::string message) : Derived1(message), Derived2(message),
Base(message)

Base实际上也是Derived3的基类,因为它实际上是从Derived1(和Derived2)继承的。这就是虚拟继承。

如果您不希望默认构造Base,则必须在此处自行调用相应的构造函数。

即使你没有明确声明Derived3继承自Base,它也虚拟地继承它,因此你可以从Derived3调用它的构造函数。

请注意,如果你将Derived4声明为Derived3的子类,则此处不会调用Base的构造函数。Derived4将继承Base,并负责构建它。

当你有虚拟继承的类时,真正发生的事情是,你声明的每个构造函数都可以被认为是实际上产生了两个实际的构造函数,实际上:一个负责构造所有虚拟继承类的构造函数,另一个不负责构造的构造函数。这Derived3你在上面声明的构造函数:实际上你最终得到了两个构造函数。两个价格为一个:一个会建造Base,一个不会。将构造Base的那个在直接构造Derived3时使用,并且是派生最多的类。第二个构造函数是相同的,只是它不会构造Base,并且如果实例化了Derived3的子类,它就会被使用。你把它看作是一个构造函数,但编译器做了更多的工作,创建了其中的两个,并确保在需要构造某些东西时使用正确的一个。