运行下面的代码我希望获得以下控制台输出:
B int v
D
而不是那样,E的构造函数调用B的默认构造函数,结果是:
B
D
实现正确构造的一种方法是在E中重新声明D的相同构造函数(即注释的代码部分),但我仍然希望找到比这更好的解决方案。
准备运行代码,使用-std = c ++ 11标志:
#include <iostream>
class A {
public:
virtual void fun() = 0;
virtual void fun2();
void fun3();
};
class B : public A {
public:
B();
B(int);
void fun();
void fun2();
};
class C : virtual public B {
public:
using B::B;
void fun();
};
class D : virtual public B {
public:
D();
D(int);
void fun();
};
class E : public C, public D {
public:
using D::D;
void fun();
};
void A::fun2() {}
void A::fun3() {}
B::B() { std::cout << "B\n"; }
B::B(int v1) { std::cout << "B int v\n"; }
void B::fun() {}
void B::fun2() {}
void C::fun() {}
D::D() { std::cout << "D\n"; }
D::D(int v1) : B(v1) { std::cout << "D\n"; }
void D::fun() {}
/*E::E(int v1): D::B(v1){ std::cout <<"E\n";} */ void E::fun() {}
int main() {
E Eob(1);
return 0;
}
结论: 最后,为E定义显式构造函数,显式调用虚拟基类B(参见注释代码段)是必要的。
正如Eljay在第一时间正确评论的那样,我错误地使用了“使用D :: D”。 “using”关键字永远不会重新定义E的构造函数,这类似于D的构造函数;它只调用基类D的构造函数,并强制基类D构造。后一个事实触发了虚拟基类构造的层次结构(正如StoryTeller在下面回答的那样),并导致我的问题根据需要构建了E类的对象。
答案 0 :(得分:3)
这是一个相当常见的陷阱。首先让我说A
的存在是一个红鲱鱼。你可以通过完全省略它来缩短你的例子。
您没有看到使用B(int)
的原因是由于C ++标准中的两个子句。首先[class.inhctor]/8说:
隐式定义的继承构造函数执行set of 由用户编写的类的初始化 具有mem-initializer-list的该类的内联构造函数 只有mem-initializer有一个mem-initializer-id来命名基数 using声明的nested-name-specifier中表示的类 和下面指定的表达式列表,以及 函数体中的复合语句为空([class.base.init])。
其中说D
中E
继承的c'tor被翻译成这样的内容:
E::E(int i) : D(i) {}
不幸的是你的问题。因为在咨询[class.base.init/10]时:
在非委托构造函数中,初始化继续进行 以下顺序:
- 首先,仅适用于派生类最多的构造函数([intro.object]),虚拟基类按顺序初始化 它们出现在深度优先的从左到右的遍历中 基类的非循环图,其中“从左到右”是基数的顺序 派生类中基类的外观 基说明符列表。
我们看到(强调我的)它只是能够并且将初始化虚拟基础的最衍生的c'tor。那个派生得最多的人怎么做呢?正如我们之前写的那样它从其成员初始化列表中省略了虚拟基础。因此,虚拟基础默认初始化。
如果你想将一个整数传递给B
的c'tor。您需要自己定义E
的构造函数:
E::E(int i) : B(i), D(i) {}