为什么在这种情况下删除祖父母的构造函数?

时间:2018-07-20 17:46:44

标签: c++ multiple-inheritance

编译器抱怨D的构造函数由于格式不正确而被删除了吗?

#include<iostream>
using namespace std;

class A
{
   int x;
   public:
   A(int i) { x = i; }
   void print() { cout << x; }
};

class B: virtual public A
{
   public:
      B():A(10) { }
};

class C: virtual public A 
{
   public:
      C():A(10) { }
};

class D: public B, public C {
};

int main()
{
   D d;
   d.print();
   return 0;
}

输出

  

main.cpp:37:4:错误:使用已删除的函数'D :: D()'D d;       ^ main.cpp:32:7:注意:'D :: D()'被隐式删除,因为默认定义格式不正确:D类:public B,public C {          ^

3 个答案:

答案 0 :(得分:6)

由于虚拟基类的初始化规则,

class D: public B, public C {
};

等效于:

class D: public B, public C {
    public:
       D() : A(), B(), C() {}
};

这就是为什么您不能在D的实例中创建的原因。

解决方案1 ​​

更改A,使其具有默认构造函数。

class A
{
     int x;
   public:
     A(int i = 0) { x = i; }
     void print() { cout << x; }
};

解决方案2

D更改为:

class D: public B, public C {
    public:
       D() : A(0), B(), C() {}
};

或更简单的版本

class D: public B, public C {
    public:
       D() : A(0) {}
};

答案 1 :(得分:2)

这是因为D使用A间接继承自virtual。 A没有无参数的构造函数,因此无法为D生成由编译器生成的构造函数。

答案 2 :(得分:0)

注意:这主要是在添加标准参考,以防万一有人关心(但对他而言,@ R。Sahu的回答很准确)。

标准规定([class.base.init] / 13):

  

在非委托构造函数中,初始化在   
(13.1)—首先,并且仅用于   在大多数派生类(6.6.2)中,虚拟基类在   它们在深度优先从左到右遍历时出现的顺序   基类的有向无环图,其中“从左到右”是   基类在派生类中的出现顺序   base-specifier-list。
(13.2)—然后,直接基类是   按照它们出现在声明中的顺序进行初始化   基本说明符列表(与mem初始化程序的顺序无关)。

因此,由于A是一个虚拟基类,所以它是由派生程度最高的类(D)直接初始化的。直到之后,直接基类才被初始化-但是要编译的任何东西,最派生的类必须能够初始化虚拟基类。

在这样的情况下,有些人可能会发现有趣的一点。让我们稍微修改一下类结构,以便进行必要的初始化,并(重要地)在每个构造函数中使用唯一值进行初始化:

#include <iostream>

class A {
    int i;
 public:
    A(int i) : i(i) {}

    void show() { std::cout << "value: " << i << "\n"; }
};

class B : virtual public A{
public:
    B() : A(10) {}
};

class C : virtual public A { 
public:
    C() : A(20) {}
};

class D : public B, public C {
public:
    D() : A(0) {}
};

int main() {
    D d;
    d.show();
}

在这种情况下,究竟会发生什么?我们有3个不同的构造函数,每个构造函数都“想”要用不同的值初始化A对象吗?哪一个“获胜”?

答案是,派生自最广泛的构造函数(D :: D)中的一个是用来初始化虚拟基类对象的,因此就是“获胜”的那个。当我们运行上面的代码时,它应该显示0