【问题标题】:Behaviour of virtual inheritance in this case在这种情况下虚拟继承的行为
【发布时间】:2012-04-24 18:57:23
【问题描述】:

我用虚拟继承编写了这个程序,我有几个问题。

#include<iostream>
using namespace std;

class B1
{
 public:
  B1()
  {
    cout << "In B1 constructor\n";
  }
};

class V1 : public B1
{
 public:
  V1()
  {
    cout << "In V1 constructor\n";
  }
};

class D1 : virtual public V1
{
 public:
  D1()
  {
    cout << "In D1 constructor\n";
  }
};

class B2
{
 public:
  B2()
  {
    cout << "In B2 constructor\n";
  }
};

class B3 {
 public:
  B3()
  {
    cout << "In B3 constructor\n";
  }
};

class V2 : public B1, public B2
{
 public:
  V2()
  {
    cout << "In V2 constructor\n";
  }
};

class D2 : public B3, virtual public V2
{
 public:
  D2()
  {
    cout << "In D2 constructor\n";
  }
};

class X : public D1, virtual public D2
{
 public:
  X()
  {
    cout << "In X constructor\n";
  }
};

int main()
{
  X x;
  return 0;
}

程序的输出:

In B1 constructor
In V1 constructor
In B1 constructor
In B2 constructor
In V2 constructor
In B3 constructor
In D2 constructor
In D1 constructor
In X constructor

我希望得到这样的输出:

In B1 constructor
In B2 constructor
In V2 constructor
In B2 constructor
In D2 constructor
In B1 constructor
In V1 constructor
In D1 constructor
In X constructor

基于先构造一个虚拟基类的对象,再构造另一个基类对象。有人可以解释这种行为吗?

【问题讨论】:

  • 拥抱?你想要一个B2 子对象被构造两次???

标签: c++ multiple-inheritance


【解决方案1】:

标准中的准确引用是 12.6.2p10:

在非委托构造函数中,初始化按以下顺序进行:

——首先,并且仅对于最派生类 (1.8) 的构造函数,虚拟基类按照它们在有向无环图的深度优先从左到右遍历中出现的顺序进行初始化基类,其中“从左到右”是基类在派生类基说明符列表中的出现顺序。

——然后​​,直接基类按照它们出现在 base-specifier-list 中的声明顺序进行初始化(无论 mem-initializers 的顺序如何)。

——然后​​,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样不管 mem-initializers 的顺序)。

——最后,构造函数体的复合语句被执行。

我相信关键是粗体文本中的 从左到右的深度优先。 V1 类是X 的虚拟基础,位于V2 的左侧,即使它在层次结构中更深。

您的层次结构图如下:

            X
          /   \\
        D1     D2
        ||    / \\
        V1  B3   V2
        |       /  \
        B1    B1*   B2

单行表示普通继承,双行表示虚拟继承。请注意,您的完整对象X 中有两个B1 实例。现在,如果您执行 从左到右的深度优先搜索,您将按以下顺序遍历节点:

[ B1, V1, D1, B3, B1*, B2, V2, D2, X ]

而虚拟基地是V1、V2、D2,这是它们将被构造的顺序。 V1 需要构造 B1。 V2需要B1*和B2的构造,D2需要B3,所以顺序应该是:

[ B1, V1, B1*, B2, V2, B3, D2, D1, X ]

其中B1 构造由V1 触发,B1* 和B2 必须在V2 之前排序,B3 作为D2 的依赖项触发。至此,所有的虚拟基地都建好,非虚拟基地开始建设。 X 的唯一非虚基由于虚基的依赖关系而未被初始化的是D1。

如果菱形是闭合的(假设V1 和V2 几乎继承自B1,那么将只有一个B1 的实例,它将是第一个要构造的子对象。

【讨论】:

  • 虚拟基类按照它们出现在基类的有向无环图的深度优先从左到右遍历的顺序进行初始化......对我来说似乎有点难以理解: (
  • 如果你把你可能已经绘制的图表的镜像,现在说V2在V1的左边,B2和B3也出现在图表的左边作为它的钻石结构
  • 标准规定其中“从左到右”是派生类基类指定列表中基类的出现顺序。在这种情况下,“从左到右”的定义很好。
  • 如果分支 D2 和 D1 互换为菱形会怎样。他们有什么特别的理由吗?
  • class X : public D1, virtual public D2 你的意思是因为这个声明?但是这里将 D2 定义为虚拟不是意味着应该先构造它吗?
【解决方案2】:

C++ 总是首先构造“first”或“most base”类。然后它依次遍历继承树并构造每个连续的派生类。

【讨论】:

  • 这具有菱形结构。你能告诉我优先选择哪条路径以及基于什么?
  • @Ritesh:需要明确的是,该层次结构中没有 diamond。有多个B1 基数。
猜你喜欢
  • 2012-05-19
  • 1970-01-01
  • 2017-12-12
  • 1970-01-01
  • 2020-09-26
  • 2012-11-01
  • 1970-01-01
  • 2014-05-05
  • 1970-01-01
相关资源
最近更新 更多