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

Crash in constructor with using virtual inheritance and delegating constructors

本文关键字:构造函数 崩溃 虚拟 继承      更新时间:2023-10-16
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 编译器中的错误还是我在代码中犯了错误?

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

MSVC++构造函数有一个隐藏的布尔参数,该参数告诉构造函数它是在构造派生最多的对象(true(还是嵌入的基子对象(false(。在此示例中,只有A::A()应接收此隐藏参数中的true,而所有较低级别的构造函数调用都应接收false。然而,当从B::B(int)调用B()时,编译器无条件地传递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(基B,3(基D1

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

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

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

struct B : public D2
{
    B() : B(0) { }
    B(int val) { }
    void m() const { }
};