struct D
{
virtual void m() const = 0;
};
struct D1 : public virtual D { };
struct D2 : public virtual D { };
struct B : public D2
{
B() { }
B(int val) : B() { }
void m() const { }
};
struct A : public B, public D1
{
A() : B(0) { }
};
int main()
{
A a;
return 0;
}
我使用上面的代码使用MSVC 2013编译器崩溃了。使用GCC 4.7.2编译时,它运行时没有崩溃。类的层次结构如下所示。
D
/ \
D1 D2
| |
\ B
\ /
A
这是MS编译器中的错误,或者我在代码中犯了错误?
答案 0 :(得分:6)
快速检查MSVC ++ 2013编译器生成的汇编代码,表明从B::B(int)
到B()
的委托调用不正确。这是编译器中的一个错误。
MSVC ++构造函数有一个隐藏的布尔参数,告诉构造函数它是构造一个派生程度最高的对象(true
)还是一个嵌入式基础子对象(false
)。在此示例中,只有A::A()
应在此隐藏参数中收到true
,而所有较低级别的构造函数调用都应收到false
。但是,从B()
调用B::B(int)
时,编译器会无条件地将1
(true
)作为隐藏参数传递。这是不正确的。
; Code for `B::B(int)`
...
00F05223 push 1 ; <- this is the problem
00F05225 mov ecx,dword ptr [this]
00F05228 call B::B (0F010F0h) ; <- call to `B::B()`
...
在编译器进行委托构造函数调用时正确生成的代码中,它应该传递从调用者接收的参数值,而不是硬编码的1
。
此示例中从A::A()
进行的直接子构造函数调用的顺序如下:1)公共虚拟基础D
,2)base B
,3)base {{ 1}}
根据语言规则,在这种情况下,D1
的构造函数和B
的构造函数不应构造其虚拟基础D1
。基础D
已由最派生的对象D
在此处构建。这正是由隐藏的布尔参数控制的内容。但是,当从A
调用B::B()
时,编译器会传递一个不正确的参数值(硬编码B::B(int)
),这会导致1
错误地认为它构造的派生最多宾语。转而使B::B()
重新构建公共虚拟基础B
。这种重建将覆盖D
已经完成的正确构造的结果。这会导致崩溃。
答案 1 :(得分:0)
据我所知,您的代码示例应该有效。
但是,不过,您的构造函数委派可能被认为是不好的做法。你应该有一个完全定义的构造函数,所有较小的构造函数都委托给它,而不是相反。例如:
struct B : public D2
{
B() : B(0) { }
B(int val) { }
void m() const { }
};