繁体   English   中英

这是VC ++ 2010中的错误吗?

[英]Is this a bug in VC++ 2010?

当我在C ++中尝试钻石问题时遇到了这个问题。 以下程序对我而言在Visual Studio 2010中有效,并且得到以下输出。

D
D
A
A
B
Error7
C
C
5
7
7
Press any key to continue . . .

如果您有任何解释,请向我解释。

注意,任何B对象都绝不会以任何方式构造。 大多数派生类D仅从C和A派生。 不是B。我想知道m_i2从哪里获得值7?

#include "stdafx.h"
#include <iostream>
using namespace std;
class A{ int m_i1;
public:
    A(int i1){ m_i1 = i1;}  
    int GetMI1(){ return m_i1;} 
    void printABCD(){cout << "A" << endl;}
};
class B : public   A {
    int m_i2;
public:
    B(int i1):A(i1){} 
    void printABCD(){cout << "B" << endl;}
    void printEFGH(){cout << "Error" << m_i2 << endl;}
};

class C : virtual public A{
public:
    C(int i1):A(i1){}
    void printABCD(){cout << "C" << endl;}

};

class D : public C, public virtual A{
public:
    D(int i1):C(i1+1),A(i1){}
    void printABCD(){cout << "D" << endl;}
};

int _tmain(int argc, _TCHAR* argv[])
{
    A a(5); D d(7); C c(7); D* e = new D(7);
    d.printABCD();
    e->printABCD();
    ((A)d).printABCD();
    ((A*)e)->printABCD();
    ((B*)e)->printABCD();
    ((B*)e)->printEFGH();  // This line works perfectly, but why??
    //((B)(d)).printABCD(); //This will error and its right
    ((C*)e)->printABCD();
    ((C)d).printABCD();
    cout << a.GetMI1() << endl;
    cout << c.GetMI1() << endl;
    cout << d.GetMI1() << endl;
    system("pause");
    return 0;
}

Undefined behaviour is undefined. 解除引用(B*)e的那一刻,您的程序就具有未定义的行为(因为e实际上并未指向B对象),因此任何事情都可能发生。 该程序似乎可以运行,可能会崩溃,可以在线订购披萨。

对该行为的一种可能解释可能是D的布局恰好使得C子对象位于内存中的首位,并且具有与int相同的大小(它可能仅包含指向虚拟基数A的指针),并且其后是A子对象的成员m_i1D对象起点的偏移量与成员m_i2B对象起点的偏移量相同。 因此,代码在执行B::printEFGH()A::m_i1解释为B::m_i2 但这纯粹是猜测。 与任何UB一样,几乎任何事情都可能发生。

编译器正在生成的代码完全可以执行您所要执行的操作(并且您正在“告诉”它以调用未定义的行为)。 该函数调用你是不是虚拟的,因此有编译器可以(而且会)生成代码,只需按下this到堆栈并调用一个成员函数,字面上的call ,而不需要vtable的分辨率。

这是完全不确定的行为 ,我希望这很清楚,所以不要这样做。 您获得的值是发生在与要传递的对象的基址偏移的内存中的任何值( 不是 B派生)。

如果您想看到一些奇怪的东西(同样是UB)。 class D添加一个int成员m_dval ,然后在D结构上将其设置为42,然后UB是否完全按照您的要求进行调用。 我认为这可能会让您感到惊讶,因为m_i2成员值(不是,btw)被报告m_i2

首先,您的代码包含行为未定义的无效调用。

D类的祖先中没有B类。 这意味着从D *B *任何强制转换以及随后使用结果指针指向B类型对象的任何尝试均已无效。 如果您使用C ++样式的static_caststatic_cast ),则编译器会告诉您有关它的信息(就像它对(B)(d)尝试所做的那样)。 尝试分析此类转换后执行的任何调用没有任何意义。 即这个

((B*)e)->printABCD();
((B*)e)->printEFGH();

具有未定义的行为。

其次,您说您正在“用C ++进行钻石问题的实验”。 您的代码中与“钻石问题”有关的唯一部分实际上是此调用

cout << d.GetMI1() << endl;

它显示了D(7) -> A(7)初始值设定项胜过D(7) -> C(8) -> A(8)初始值设定项。 其余的调用与任何“钻石”效果完全无关。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM