在这种情况下虚拟继承的行为 [英] Behaviour of virtual inheritance in this case
问题描述
我用虚拟继承来写这个程序,我有几个问题。
#include< iostream>
using namespace std;
class B1
{
public:
B1()
{
cout< In B1 constructor\\\
;
}
};
class V1:public B1
{
public:
V1()
{
cout< In V1 constructor\\\
;
}
};
class D1:virtual public V1
{
public:
D1()
{
cout< In D1 constructor\\\
;
}
};
class B2
{
public:
B2()
{
cout< In B2 constructor\\\
;
}
};
class B3 {
public:
B3()
{
cout< 在B3 constructor\\\
;
}
};
class V2:public B1,public B2
{
public:
V2()
{
cout< In V2 constructor\\\
;
}
};
class D2:public B3,virtual public V2
{
public:
D2()
{
cout< 在D2 constructor\\\
;
}
};
class X:public D1,virtual public D2
{
public:
X()
{
cout< In X constructor\\\
;
}
};
int main()
{
X x;
return 0;
}
程序输出:
$ b b在B1构造函数中
在V1构造函数中
在B1构造函数中
在B2构造函数中
在V2构造函数中
在B3中构造函数
在D2构造函数中
在D1构造函数中
在X构造函数中
我期望这样的输出:
在B1构造函数中
在B2构造函数中
在V2构造函数
在B2构造函数中
在D2构造函数中
在B1构造函数中
在V1构造函数中
在D1构造函数中
在X构造函数中
,因为首先构造虚拟基类的对象,然后创建另一个基类对象。
标准的确切报价是12.6.2p10:
在非委托构造函数中,初始化按以下顺序进行:
- 首先,最大派生类(1.8)的构造函数,虚拟基类按照它们在基类的有向无环图的深度优先从左到右遍历中出现的顺序进行初始化,其中left -to-right是派生类base-specifier-list中基类的出现顺序。
- 然后,直接基类按声明顺序初始化因为它们出现在base-specifier-list中(不管mem初始化器的顺序如何)。
- 然后,非静态数据成员按顺序初始化它们在类定义中被声明(再次不考虑mem初始化器的顺序)。
- 最后,构造函数体的复合语句被执行。 / p>
我相信关键是在粗体文本中的深度优先从左到右。 V1
类是 X
的虚拟基础,即
在你的情况下,层次结构图就是c $ c> V2 如下:
X
/ \\
D1 D2
|| / \\
V1 B3 V2
| / \
B1 B1 * B2
其中单行标识平原继承,是虚拟继承。请注意,在您的完整对象 X
中有两个实例 B1
。现在,如果您执行深度优先从左到右搜索,您将按照以下顺序遍历节点:
[B1,V1,D1,B3,B1 *,B2,V2,D2,X]
$ b b
虚拟基础是 V1
, V2
, D2
,这是它们将被构造的顺序。 V1
需要构建 B1
。 V2
需要构建 B1 *
和 B2
code> D2 需要 B3
,因此订单应为:
[B1,V1,B1 *,B2,V2,B3,D2,D1,X]
其中 B1
构造由 V1
, B1 *
code>作为 V2
, B3
之前必须订购 D2
的依赖项触发。在这一点上,构建所有虚拟碱基并开始构建非虚拟碱基。由于虚拟库的依赖关系尚未初始化的 X
的唯一非虚拟库是 D1
。
如果钻石已关闭(例如 V1
和 V2
虚拟地从 B1
继承,则只有一个 B1
的实例,它将是第一个子对象
I wrote this program with virtual inheritance and I have a few questions.
#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;
}
Output of the program:
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
I expected an output like this:
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
on the basis that an object of a virtual base class is constructed first and then the other base class object. Can Someone explain this behaviour?
The exact quote from the standard is 12.6.2p10:
In a non-delegating constructor, initialization proceeds in the following order:
— First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where "left-to-right" is the order of appearance of the base classes in the derived class base-specifier-list.
— Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
— Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
— Finally, the compound-statement of the constructor body is executed.
I believe the key is the depth-first left to right in the bolded text. The V1
class is a virtual base of X
that is to the left of V2
, even if it is deeper in the hierarchy.
The hierarchy graph in your case is like:
X
/ \\
D1 D2
|| / \\
V1 B3 V2
| / \
B1 B1* B2
Where single lines identify plain inheritance, and double lines are virtual inheritance. Note that there are two instances of B1
in your complete object X
. Now if you perform a depth-first left-to-right search you will walk the nodes in the following order:
[ B1, V1, D1, B3, B1*, B2, V2, D2, X ]
And the virtual bases are V1
, V2
, D2
, which is the order in which they will be constructed. V1
requires the construction of B1
. V2
requires the construction of B1*
and B2
, D2
requires B3
, so the order should be:
[ B1, V1, B1*, B2, V2, B3, D2, D1, X ]
Where B1
construction is triggered by V1
, B1*
and B2
must be ordered before V2
, B3
is triggered as a dependency of D2
. At this point all of the virtual bases are built and the non-virtual bases start to be constructed. The only non-virtual base of X
that has not been initialized due to the dependencies of the virtual bases is D1
.
If the diamond was closed (say that V1
and V2
inherited virtually from B1
, then there would be only one instance of B1
and it would be the first subobject to be constructed.
这篇关于在这种情况下虚拟继承的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!