同班,不同大小......?

时间:2010-11-04 14:22:41

标签: c++ visual-c++ visual-studio-2005

查看代码,然后你会理解我的困惑。

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* pATest* pB都有成员变量m_Test []。

如果来自Test的班级Bho.cpp的尺寸为1,那么pB很奇怪,不是吗?

我不明白发生了什么,请告诉我。 提前谢谢。

环境: Microsoft Visual Studio 2005 SP1(或SP2?)

4 个答案:

答案 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)

像许多人在这里讲的那样,你违反了所谓的一元定义规则(ODR)。

了解C / C ++程序的组装方式非常重要。也就是说,翻译单元(cpp文件)分别编译为 ,而没有任何相互连接。下一个链接器根据编译器生成的符号和代码片段组装可执行文件。它没有任何高级类型信息,因此无法(也不应该)检测到问题。

所以你实际上欺骗了编译器,打败了自己,射击你的脚,无论你喜欢什么。

我要提到的一点是,由于各种包含头文件和其他定义的细微变化,实际上违反了ODR规则,但通常没有问题,人们甚至没有意识到这一点。 / p>

例如,一个结构可能有一个LPCTSTR类型的成员,它是指向charwchar_t的指针,具体取决于定义,包括等等。但是这种类型的违规是“几乎可以”。只要您不在不同编译的翻译单元中实际使用此成员,就没有问题。

还有许多其他常见的例子。一些来自类内实现的成员函数(内联),它实际上在不同的翻译单元中编译成不同的代码(例如,由于不同的翻译单元的编译器选项不同)。

然而这通常没问题。但是,在您的情况下,结构的内存布局已更改。在这里,我们遇到了一个真正的问题。