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

前端之家收集整理的这篇文章主要介绍了c – 使用虚拟继承和委托构造函数在构造函数中崩溃前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
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()已经进行的正确构造的结果.后来这会导致崩溃.

猜你在找的C&C++相关文章