[英]Another BUG of VC++ 2010? About declaring a constant REFERENCE in a header
[英]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_i1
与D
对象起点的偏移量与成员m_i2
与B
对象起点的偏移量相同。 因此,代码在执行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_cast
( static_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.