查看代码,然后你会理解我的困惑。
Test.h
class Test {
public:
#ifndef HIDE_VARIABLE
int m_Test[10];
#endif
};
Aho.h
class Test;
int GetSizeA();
Test* GetNewTestA();
Aho.cpp
//#define HIDE_VARIABLE
#include "Test.h"
#include "Aho.h"
int GetSizeA() { return sizeof(Test); }
Test* GetNewTestA() { return new Test(); }
Bho.h
class Test;
int GetSizeB();
Test* GetNewTestB();
Bho.cpp
#define HIDE_VARIABLE // important!
#include "Test.h"
#include "Bho.h"
int GetSizeB() { return sizeof(Test); }
Test* GetNewTestB() { return new Test(); }
TestPrj.cpp
#include "Aho.h"
#include "Bho.h"
#include "Test.h"
int _tmain(int argc, _TCHAR* argv[]) {
int a = GetSizeA();
int b = GetSizeB();
Test* pA = GetNewTestA();
Test* pB = GetNewTestB();
pA->m_Test[0] = 1;
pB->m_Test[0] = 1;
// output : 40 1
std::cout << a << '\t' << b << std::endl;
char temp;
std::cin >> temp;
return 0;
}
Aho.cpp没有#define HIDE_VARIABLE
,所以GetSizeA()
返回40,但是
Bho.cpp执行#define HIDE_VARIABLE
,因此GetSizeB()
返回1。
但是,Test* pA
和Test* pB
都有成员变量m_Test []。
如果来自Test
的班级Bho.cpp
的尺寸为1,那么pB很奇怪,不是吗?
我不明白发生了什么,请告诉我。 提前谢谢。
环境: Microsoft Visual Studio 2005 SP1(或SP2?)
答案 0 :(得分:13)
您的代码显示未定义的行为。您违反了one definition rule(类Test
在两个地方的定义不同)。因此,允许编译器做任何想做的事情,包括“怪异”的行为。
答案 1 :(得分:13)
您违反了一个定义规则(ODR)的要求。程序的行为未定义。这是唯一正在发生的事情。
根据ODR,必须在所有翻译单元中定义具有外部链接的类相同。
答案 2 :(得分:2)
除了ODR。
大部分的悲痛是由于只在cpp文件中包含标题引起的,允许你更改编译单元之间的定义。
但是,Test * pA和Test * pB都有成员变量m_Test []。
不,pB没有m_Test[]
但是TestPrj编译单元不知道并且正在应用类的错误结构,因此它将编译。
除非你在调试中编译并捕获内存溢出,否则你很多时候都不会看到问题
pB->m_Test[9] = 1;
将导致写入未由pB分配的内存,但可能是也可能不是您写入的有效空间。
答案 3 :(得分:1)
了解C / C ++程序的组装方式非常重要。也就是说,翻译单元(cpp文件)分别编译为 ,而没有任何相互连接。下一个链接器根据编译器生成的符号和代码片段组装可执行文件。它没有任何高级类型信息,因此无法(也不应该)检测到问题。
所以你实际上欺骗了编译器,打败了自己,射击你的脚,无论你喜欢什么。
我要提到的一点是,由于各种包含头文件和其他定义的细微变化,实际上违反了ODR规则,但通常没有问题,人们甚至没有意识到这一点。 / p>
例如,一个结构可能有一个LPCTSTR
类型的成员,它是指向char
或wchar_t
的指针,具体取决于定义,包括等等。但是这种类型的违规是“几乎可以”。只要您不在不同编译的翻译单元中实际使用此成员,就没有问题。
还有许多其他常见的例子。一些来自类内实现的成员函数(内联),它实际上在不同的翻译单元中编译成不同的代码(例如,由于不同的翻译单元的编译器选项不同)。
然而这通常没问题。但是,在您的情况下,结构的内存布局已更改。在这里,我们遇到了一个真正的问题。