这是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_i1
与D
对象开始的偏移量与成员m_i2
将具有B
对象启动。因此,代码将该A::m_i1
解释为执行B::printEFGH()
时B::m_i2
。但这纯粹是猜测。与任何UB一样,实际上任何事情都可能发生。
编译器生成的代码完全按照你告诉它做的事情(你"告诉它"调用未定义的行为(。您调用的函数不是虚拟的,因此编译器可以(并且将(生成代码,只需将this
推送到堆栈并调用成员函数,字面意思是不需要vtable
解析的call
。
这是完全未定义的行为,我希望这是清楚的,所以不要这样做。你得到的值是内存中恰好与你传递的对象基数偏移量(不是 B 派生(的任何值。
如果你想看到一些奇怪的东西(同样是UB(。 添加一个int
成员m_dval
到class D
,然后在D构造上将其设置为42,然后UB调用是否完全按照您的要求。我认为报告为m_i2
成员值的内容可能会让您感到惊讶(顺便说一句,这不是(。
首先,您的代码包含行为未定义的无效调用。
D
类在其祖先中没有B
类。这意味着从 D *
到 B *
后的任何尝试使用生成的指针指向 B
类型的对象的任何强制转换都已无效。如果你使用C++式强制转换(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)
初始值设定项。其余的调用与任何"钻石"效果完全无关。
- 带有指向成员函数的指针的模板 =VC++ 2017 和 gcc 5.1 的>不同的错误消息
- VC++ C2011 重定义错误 - 未使用的头文件
- VC 中的运行时错误,但不在GCC中
- 如何在 VC++ 中通过引用传递另一个对象的方法(错误 C2664)
- VC 链接器错误使用Dllimport/DLLEXPORT宏将标题包括在多个项目中
- 使用FP:快速导致错误的VC 结果(不仅仅是不准确)结果 - 这是编译器错误
- 指向全局运算符的函数指针在 VC++ 上编译,而 clang 给出错误
- 显示 VC++ 中的错误的 Visual C++ 数组副本
- VC 错误,带有声明和通用引用或未定义的行为
- VC 15调用错误的复制构造函数以捕获lambda
- VC XTREE内部标头的语法错误,带有QT/Boost Project
- VC++的一个大错误?为什么初始值设定项列表不对结构进行值初始化
- VC++ 标准库链接错误
- VC++ 可变模板模板错误 C2244:无法将函数定义与现有声明匹配
- 错误 PRJ0002:错误结果 -1073741515 从"C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\cl.
- VC glew外部链接错误
- VC++使用gumbo查询时出现问题(链接错误)
- visual Odd编译器C++中的错误(VC编译器)
- 定义模板时出现多重定义错误(vc++fine)
- 将我的类移动到目录后出现链接错误"vc/include/"