(Why) 纯虚派生class需要虚基class构造函数调用吗?

(Why) Is virtual base class constructor call required in pure virtual derived class?

我有一个 class 层次结构,带有 菱形 结构和一个基础 class 没有默认构造函数 ,因为它有参考成员。

代码如下所示:

class Base
{
public:
    Base( CustomType& obj ) : refObj_( obj ) {}
    virtual ~Base() {}
private:
    CustomType& refObj_;
};

class Left : public virtual Base
{
public:
    Left() {}; // ERROR: Compiler requests calling Base's constructor
    virtual void leftsMethod() = 0;
};

class Right : public virtual Base
{
public:
    Right( CustomType& obj ) : Base( obj ) {}; // Compiles, but Base( obj ) never gets called here
    virtual void rightsMethod() = 0;
};

class Bottom : public Left, public Right
{
public:
    Bottom( CustomType& obj ) : Base( obj ), Left(), Right( obj ) {}
};

编辑:添加了虚基析构函数(原代码有)

注意LeftRight是纯虚classes,即在没有 在某些情况下,它们的构造函数会调用 Base 的构造函数。 这是由于虚拟继承,意味着最派生的 class Bottom 调用 Base 的构造函数。

我的问题:为什么我仍然必须在Left的初始化列表中调用Base的构造函数?我想避免这种情况并将引用直接从 Bottom 的构造函数传递给 Base。 (我使用的是 MSVC 2010s "half C++11" 编译器。)

对于这种虚拟继承和基类中引用的组合,是否有另一种解决方案class?

您没有 Base 的默认构造函数,但是 Left 在创建 Left 对象(不是 Bottom一个)。

问题是您在 Base 中有一个引用,这意味着您没有可能的简单构造函数。

我会说你有一个很大的设计问题,因为 obj 是由 Right 初始化的,所以后者应该持有 obj,而不是 Base

另外 Base 的虚拟析构函数在哪里?

class Base
{
public:
    Base() = default;
    ~Base() {}
};

class Left : public virtual Base
{
public:
    virtual void leftsMethod();
};

class Right : public virtual Base
{
    int& refObj_;
public:
    Right( int& obj ) : refObj_( obj ) {}
    virtual void rightsMethod();
};

class Bottom : public Left, public Right
{
public:
    Bottom( int& obj ) : Right( obj ) {}
};

官方文档中的一段话可以描述这种情况:

Practically speaking, this means that when you create a concrete class that has a virtual base class, you must be prepared to pass whatever parameters are required to call the virtual base class’s constructor. And, of course, if there are several virtual base classes anywhere in your classes ancestry, you must be prepared to call all their constructors. This might mean that the most-derived class’s constructor needs more parameters than you might otherwise think.

看到这个FAQ

撇开设计问题(这里显然存在)不谈,我完全同意你的推理,因为 Left 是一个抽象 class,所以 Left 构造函数将永远不必调用任何 Base 构造函数,因此它是必需的很奇怪。

事实上,我用几个编译器版本测试了你的代码,它确实可以编译 gcc 7.1 and onwards, with clang 3.4.1 and onwards, and with all available msvc 版本。

因此,我的假设是,这只是早期编译器版本中的一个错误。谁能证实这一点?

另请注意,如果将 virtual void leftsMethod() = 0; 更改为 virtual void leftsMethod() {},使 Left 不再抽象,则错误 returns,即使使用 latest versions.这完全有道理,因为现在您 可以 实例化一个 Left 实例,因此 Left 的构造函数可以调用 [=13] 之一=]的构造函数。

可能的解决方法

如果您无法切换到较新的编译器并且无法更改 Base 的实现,这可能是适合您的解决方案:

在某处定义一个 CustomType 的虚拟实例。然后,您可以像这样提供 "required" 默认构造函数:

class CustomType{};

class Base
{
public:
    Base( CustomType& obj ) : refObj_( obj ) {}
private:
    CustomType& refObj_;
};

static CustomType dummy;

class Left : public virtual Base
{
protected:
    Left() : Base(dummy) {}; // note here
public:
    virtual void leftsMethod() = 0;
};

class Right : public virtual Base
{
protected:
    Right() : Base(dummy) {}; // and here
public:
    virtual void rightsMethod() = 0;
};

class Bottom : public Left, public Right
{
public:
    Bottom( CustomType& obj ) : Base( obj ), Left(), Right() {}
    virtual void leftsMethod() override {}
    virtual void rightsMethod() override {}
};

void test()
{
    CustomType c;
    Bottom b(c);
}

这只是为了让编译器高兴,您关于这些构造函数永远不会调用 Base(dummy) 的论点仍然成立。

但是请注意,Base 确实应该有一个虚拟析构函数!我不知道您是否只是为了简洁而没有在此处包含它,或者它是否真的没有。如果没有,那么在它之上构建一个 class 层次结构是一个非常糟糕的主意。