微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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()已经进行的正确构造的结果.后来这会导致崩溃.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐