使用具有虚拟多重继承的基础构造函数

时间:2017-12-26 15:37:10

标签: c++ c++11 virtual-inheritance

运行下面的代码我希望获得以下控制台输出:

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类的对象。

1 个答案:

答案 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])。

其中说DE继承的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) {}