7

考虑下面的代码:

#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

因此,对于 line类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

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

4

4 回答 4

10

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

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

现在,在您的复制构造函数中,明确指定AandB的复制构造函数C应该被调用:

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)的构造函数,虚拟基类按照它们出现在基类的有向无环图的深度优先从左到右遍历的顺序进行初始化,其中“左- to-right”是派生类base-specifier-list中基类的出现顺序。

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

于 2017-04-30T05:46:32.840 回答
3

您继承类的方式,它们都使用private继承。

B通过将fromACfrom的继承更改Aprotectedor public,可以解决问题。

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继承。

于 2017-04-30T05:49:13.317 回答
0

Alternative solution which doesn't require changing the inheritance modifiers of class B or C:

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";}
};
于 2017-05-07T05:24:46.467 回答
0

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

子句 [class.access] 中的所有访问控制都会影响从特定实体的声明中访问类成员名称的能力…… [注意:这种访问也适用于对构造函数、转换函数和析构函数的隐式引用。——尾注]

同样,[class.access]/4

特殊成员函数遵循通常的访问规则。[ 示例:声明构造函数 protected 确保只有派生类和朋友可以使用它创建对象。—结束示例]

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

在非委托构造函数中,如果给定的潜在构造子对象不是由 mem-initializer-id 指定的(包括由于构造函数没有 ctor-initializer 而没有 mem-initializer-list 的情况),那么 ...否则,实体默认初始化

基类子对象缺少任何ctor-initializer意味着子对象是默认初始化的;来自[dcl.init]/7

对 T 类型的对象进行默认初始化意味着: ... 这样选择的构造函数被调用,带有一个空参数列表,以初始化该对象。

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

没有提到基类没有区别;在任何情况下,构造函数都没有名称,也没有在ctor-initializer中命名,它被显式或隐式引用。标准中没有任何内容表明在这种情况下不应执行访问控制。

似乎构造函数来自一个不可访问的基类,不能以任何一种方式调用,所以你的程序不应该编译。

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

class D: B, C, virtual A
{

这样,虚拟基类A仍然是私有的,但可以被D.

于 2017-05-08T03:14:17.807 回答