如何调用所有基本类的复制构造函数,以在C 中复制钻石继承中最派生的类对象

How to call copy constructor of all base classes for copying most derived class object in diamond inheritance in C++?

本文关键字:复制 何调用 以在 钻石 继承 对象 派生 构造函数 调用      更新时间:2023-10-16

考虑以下代码:

#include<iostream>
using namespace std;
class A
{
public:
     A() {cout << "1";}
     A(const A &obj) {cout << "2";}
};
class B: virtual A
{
public:
    B() {cout << "3";}
    B(const B & obj) {cout<< "4";}
};
class C: virtual A
{
public:
   C() {cout << "5";}
   C(const C & obj) {cout << "6";}
};
class D:B,C
{
public:
    D()  {cout << "7";}
    D(const D & obj) {cout << "8";}
};
int main()
{
   D d1;
   cout << "n";
   D d(d1);
}

程序的输出如下:

1357
1358

因此,对于D d(d1)行,D类的复制构造函数被调用。在继承期间,我们需要明确调用基类的复制构造函数,否则仅调用基类的默认构造函数。我知道直到这里。

我的问题:

现在,我想在D d(d1)执行期间调用所有基类的复制构造函数。为此,如果我在下面尝试 D(const D & obj) : A(obj), B(obj), C(obj) {cout << "8";}然后我得到这个错误:错误:'class A A::A' is inaccessible within this context

如何解决问题。我想要ABC的复制构造函数,当D的复制构造函数被调用时。这可能是很小的变化,但我没有得到。

首先,让我们更改您的继承,因为当前是私有的:

class B : virtual protected A {...};
class C : virtual protected A {...};

现在,在您的复制构造函数中,明确指定ABC的复制构造仪:

class D : protected B, protected C {
    D(const D & obj) : A(obj), B(obj), C(obj) {cout << "8";}
};

,输出将根据需要(2468(。

为什么?

当我们拥有虚拟基类时,必须由最派生的类初始化,否则将存在歧义性的,例如B还是C,例如负责A的构建。

§12.6.2,(13.1(:

在非规定的构造函数中,初始化按以下顺序进行:

  • 首先,仅适用于最派生的类(1.8(的构造函数,虚拟基类按照出现在深度优先的左右横向横穿的定向无环图基础类别,其中"从左到右"是派生类基准列表中的基类。

特别是,如果您定义了复制构造函数,并省略了应该调用的复制构造函数列表,则将使用使用默认的构造函数

您继承了类的方式,所有这些都使用private继承。

通过将B的继承从AA从CC_21更改为protectedpublic,您可以解决问题。

class B : protected virtual A
{
   ...
}
class C : protected virtual A
{
   ...
}

class B : public virtual A
{
   ...
}
class C : public virtual A
{
   ...
}

然后更新D的复制构造函数为:

D(const D & obj) : A(obj), B(obj), C(obj) {cout <<"8";}

ps 对我来说,即使使用private继承,默认的构造函数也使我感到困惑。

替代解决方案不需要更改类BC的继承修饰符:

class A
{
public:
     A() {cout << "1";}
     A(const A &obj) {cout << "2";}
};
class B: virtual A
{
public:
    B() {cout << "3";}
    B(const B & obj) {cout<< "4";}
};
class C: virtual A
{
public:
   C() {cout << "5";}
   C(const C & obj) {cout << "6";}
};
class D:B,C,virtual A
{
public:
    D() {cout << "7";}
    D(const D & obj) : A(obj), B(obj), C(obj) {cout << "8";}
};

涉及构造函数的访问检查:来自[class.access]/6

所有访问控件在子句[class.access]中都会影响的能力 从特定实体的声明中访问类成员名称 ... [注意:此访问也适用于隐式引用 构造函数,转换功能和破坏者。 - 终注]

类似地,[class.access]/4

特殊会员功能遵守通常的访问规则。[ 例子: 宣布受构造函数保护可确保仅派生类 朋友可以使用它创建对象。 - 结束示例]

关于基类亚对象的初始化:来自[class.base.init]/9

在非级别的构造函数中,如果给定的潜在构造 Mem-Initializer-ID未指定子对象(包括 没有mem-Initializer-list的情况,因为构造函数 没有ctor intializer(,那么...否则,实体是 默认限制

对于基类子对象,缺乏任何 ctor-initializer 意味着该子对象是默认的。来自[dcl.init]/7

默认initialize t型的对象表示:... 因此选择的构造函数被称为,带有空参数列表, 初始化对象。

因此, ctor-initializer中缺少任何基础是该基础默认限制的请求,这意味着调用默认的构造函数。

缺乏对基类没有什么区别。无论如何,构造函数没有名称,也没有在 ctor-initializer 中命名,它被明确或隐式引用。在这种情况下,标准中没有什么都没有表明不应执行访问控制。

似乎是从无法访问的基类的构造函数都无法调用的,因此您的程序不应编译。

在任何情况下,您都可以将继承从私人更改为受保护,甚至可以在虚拟基类中添加路径:

class D: B, C, virtual A
{

以这种方式,虚拟基类A仍然是私有的,但是D可以访问。