C++虚拟基类:不调用父级的复制构造函数

C++ virtual base classes: parent's copy constructor doesn't get called

本文关键字:复制 构造函数 调用 虚拟 基类 C++      更新时间:2023-10-16

如下面的代码所示,我有三个类。注意我是如何编写复制构造函数的。

#include <iostream>
class Abstract
{
public:
    Abstract(){};
    Abstract( const Abstract& other ): mA(other.mA){};
    virtual ~Abstract(){};
    void setA(double inA){mA = inA;};
    double getA(){return mA;};
    virtual void isAbstract() = 0;
protected:
    double mA;
};
class Parent : public virtual Abstract
{
public:
    Parent(){};
    Parent( const Parent& other ): Abstract(other){};
    virtual ~Parent(){};
};

class Child : public virtual Parent
{
public:
    Child(){};
    Child( const Child& other ): Parent(other){};
    virtual ~Child(){};
    void isAbstract(){};
};

int main()
{
    Child child1;
    child1.setA(5);
    Child childCopy(child1);
    std::cout << childCopy.getA() << std::endl;
    return 0;
}

现在为什么Abstract()被调用,而不是复制构造器Abstract( const Abstract& other )时,childCopy被构造?

Child(other)不应该调用Parent(other)吗?Parent(other)不应该反过来调用Abstract(other)吗?

虚基类只能由最派生的类初始化。从非最派生类调用虚基类的构造函数将被忽略,并用默认构造函数调用替换。这是为了确保虚拟基子对象只初始化一次:

正确的代码应该将构造函数调用放在最派生的类:
Child(Child const& other)
    : Abstract(other) // indirect virtual bases are
                      // initialized first
    , Parent(other) // followed by direct bases
{ }

为了正确调用Abstract的复制构造函数,您需要在Child的复制构造函数的初始化列表中指定它。

Child( const Child& other ): Abstract(other), Parent(other) {};

演示

下面是标准中的相关引语,它指出虚拟基类的构造函数只能在最派生的类中调用。如果没有,则调用默认构造函数(——如果存在)。

(13.1

§12.6.2):

在非委托构造函数中,初始化按照以下顺序进行:

  • 首先,并且仅适用于最派生类的构造函数(1.8),类中出现的顺序初始化虚基类的有向无环图的深度优先从左到右遍历类的出现顺序为"从左到右"

特别地,这就是为什么您注意到Abstract的默认构造函数被调用的原因。

但是,为了避免这个陷阱,您可以放弃所有用户定义的复制构造函数,而依赖隐式定义的复制构造函数(这总是一个好主意)。演示2