1

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

#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

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

4

2 回答 2

3

标准的准确报价是 12.6.2p10:

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

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

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

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

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

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

您的情况下的层次结构图如下:

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

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

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

虚拟基础是V1, V2, D2,这是它们的构建顺序。V1需要建设B1. V2需要 和 的构造B1*,需要B2,所以顺序应该是:D2B3

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

B1构造由 触发且必须在之前排序的地方,V1作为的依赖项触发。至此,所有的虚拟基地都建好,非虚拟基地开始构建。由于虚拟基础的依赖关系,唯一未初始化的非虚拟基础是.B1*B2V2B3D2XD1

如果菱形是闭合的(比如说V1并且V2实际上继承自B1,那么将只有一个 的实例,B1它将是要构造的第一个子对象。

于 2012-04-24T19:14:42.140 回答
0

C++ 总是首先构造“第一个”或“最基”类。然后它按顺序遍历继承树并构造每个连续的派生类。

于 2012-04-24T19:09:06.207 回答