编译器抱怨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 { ^
答案 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
。