使用虚拟继承和委托构造函数在构造函数中崩溃

时间:2014-09-27 13:22:16

标签: c++ visual-c++ c++11 virtual-inheritance delegating-constructor

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编译器中的错误,或者我在代码中犯了错误?

2 个答案:

答案 0 :(得分:6)

快速检查MSVC ++ 2013编译器生成的汇编代码,表明从B::B(int)B()的委托调用不正确。这是编译器中的一个错误。

MSVC ++构造函数有一个隐藏的布尔参数,告诉构造函数它是构造一个派生程度最高的对象(true)还是一个嵌入式基础子对象(false)。在此示例中,只有A::A()应在此隐藏参数中收到true,而所有较低级别的构造函数调用都应收到false。但是,从B()调用B::B(int)时,编译器会无条件地将1true)作为隐藏参数传递。这是不正确的。

; 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 { }
};