简体   繁体   English

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

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

I came across this when I was experimenting with diamond problem in C++. 当我在C ++中尝试钻石问题时遇到了这个问题。 The following program works in Visual studio 2010 for me and I get the following out put. 以下程序对我而言在Visual Studio 2010中有效,并且得到以下输出。

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

Please explain this to me, if you know any explanation. 如果您有任何解释,请向我解释。

Note that, Any of B object is never constructed in any manner. 注意,任何B对象都绝不会以任何方式构造。 Most derived class D gets derived from C and A only. 大多数派生类D仅从C和A派生。 Not the B. I am wondering where is this m_i2 getting value 7 from? 不是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. The moment you dereference (B*)e , your program has Undefined Behaviour (because e doesn't actually point to a B object), so anything can happen. 解除引用(B*)e的那一刻,您的程序就具有未定义的行为(因为e实际上并未指向B对象),因此任何事情都可能发生。 The program could appear to work, it could crash, it could order pizza online. 该程序似乎可以运行,可能会崩溃,可以在线订购披萨。

One possible explanation for the behaviour could be that the layout of D happens to be such that the C subobject is first in memory and has the same size as int (it probably contains just a pointer to the virtual base A ), and that is followed by the A subobject whose member m_i1 thus has the same offset from a D object start as the member m_i2 would have of a B object start. 对该行为的一种可能解释可能是D的布局恰好使得C子对象位于内存中的首位,并且具有与int相同的大小(它可能仅包含指向虚拟基数A的指针),并且其后是A子对象的成员m_i1D对象起点的偏移量与成员m_i2B对象起点的偏移量相同。 Therefore the code interprets that A::m_i1 as B::m_i2 when executing B::printEFGH() . 因此,代码在执行B::printEFGH()A::m_i1解释为B::m_i2 But this is pure speculation. 但这纯粹是猜测。 As with any UB, literally anything can happen. 与任何UB一样,几乎任何事情都可能发生。

The compiler is generating code that does exactly what you're telling it to do (and you're "telling it" to invoke undefined behavior). 编译器正在生成的代码完全可以执行您所要执行的操作(并且您正在“告诉”它以调用未定义的行为)。 The function your invoking is not virtual, and as such there the compiler can (and will) generate code that simply pushes this on to the stack and invokes a member function, literally as a call with no need for vtable resolution. 该函数调用你是不是虚拟的,因此有编译器可以(而且会)生成代码,只需按下this到堆栈并调用一个成员函数,字面上的call ,而不需要vtable的分辨率。

It is utterly undefined behavior and I hope that is clear, so don't do it. 这是完全不确定的行为 ,我希望这很清楚,所以不要这样做。 The value you're getting is whatever happens to be in memory offset from the base of the object you're passing (which is not a B derivation). 您获得的值是发生在与要传递的对象的基址偏移的内存中的任何值( 不是 B派生)。

If you want to see something odd (and equally UB). 如果您想看到一些奇怪的东西(同样是UB)。 add an int member m_dval to class D , then set it to 42 on D-construction, then do you UB invoke exactly as you have it. class D添加一个int成员m_dval ,然后在D结构上将其设置为42,然后UB是否完全按照您的要求进行调用。 I think it may surprise you what is being reported as the m_i2 member value (which isn't, btw). 我认为这可能会让您感到惊讶,因为m_i2成员值(不是,btw)被报告m_i2

Firstly, your code contains invalid calls whose behavior is undefined. 首先,您的代码包含行为未定义的无效调用。

Class D does not have class B among its ancestors. D类的祖先中没有B类。 This means that any casts from D * to B * followed by any attempts to use the resultant pointer as pointing to an object of type B are already invalid. 这意味着从D *B *任何强制转换以及随后使用结果指针指向B类型对象的任何尝试均已无效。 If you used C++-style cast ( static_cast ), the compiler would have told you about it (as it did in response to your (B)(d) attempt). 如果您使用C ++样式的static_caststatic_cast ),则编译器会告诉您有关它的信息(就像它对(B)(d)尝试所做的那样)。 There's no point in trying to analyze any calls performed after such casts. 尝试分析此类转换后执行的任何调用没有任何意义。 Ie this 即这个

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

has undefined behavior. 具有未定义的行为。

Secondly, you said that you were "experimenting with diamond problem in C++". 其次,您说您正在“用C ++进行钻石问题的实验”。 The only part of your code that has some relation to "diamond problem" is actually this call 您的代码中与“钻石问题”有关的唯一部分实际上是此调用

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

It shows you that D(7) -> A(7) initializer won over D(7) -> C(8) -> A(8) initializer. 它显示了D(7) -> A(7)初始值设定项胜过D(7) -> C(8) -> A(8)初始值设定项。 The rest of the calls are completely unrelated to any "diamond" effects. 其余的调用与任何“钻石”效果完全无关。

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

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