8
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 编译器中的错误还是我在代码中犯了错误?

4

2 回答 2

6

对 MSVC++ 2013 编译器生成的汇编代码的快速检查表明,从B::B(int)to的委托调用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) common virtual base D, 2) base B, 3) base D1

根据语言规则,在这种情况下,构造函数B和构造函数D1不应该构造它们的 virtual base D。BaseD已由派生最多的对象在该点构造A。这正是隐藏的布尔参数所控制的。但是,当B::B()从 调用时B::B(int),编译器传递了一个不正确的参数值(硬编码的1),这导致B::B()错误地假设它构造了一个最派生的对象。这又使得B重新构建了共同的虚拟基地D。这种重新构造会覆盖 . 已经进行的正确构造的结果A::A()。稍后这会导致崩溃。

于 2014-10-06T16:50:26.713 回答
0

据我所知,您的代码示例应该可以工作。

不过,顺便说一句,您的构造函数委托可能被认为是不好的做法。您应该有一个完全定义的构造函数,所有较少定义的构造函数都委托给它,而不是相反。例如:

struct B : public D2
{
    B() : B(0) { }

    B(int val) { }

    void m() const { }
};
于 2014-10-06T16:18:01.070 回答