одного класса, другого размера ...? - PullRequest
3 голосов
/ 04 ноября 2010

Посмотри код, тогда ты поймешь, что я запутался.

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?)

Ответы [ 4 ]

13 голосов
/ 04 ноября 2010

Вы нарушили требования одного правила определения (ODR). Поведение вашей программы не определено. Это единственное, что здесь происходит.

Согласно ODR, классы с внешней связью должны быть определены одинаково во всех единицах перевода.

13 голосов
/ 04 ноября 2010

Ваш код демонстрирует неопределенное поведение. Вы нарушаете одно определение правила (класс Test определяется по-разному в двух местах). Поэтому компилятору разрешено делать все, что он хочет, включая «странное» поведение.

2 голосов
/ 04 ноября 2010

В дополнение к ODR.

Большая часть горя вызвана включением заголовков только в файлы cpp, что позволяет изменять определение между единицами компиляции.

Но Test * pA и Test * pB имеют переменную-член m_Test [].

Нет, pB не имеет m_Test[], однако модуль компиляции TestPrj этого не знает и применяетнеправильная структура класса, поэтому он будет компилироваться.Если вы не откомпилируете в отладке с захватом переполнения памяти, вы в большинстве случаев не увидите проблемы.
pB->m_Test[9] = 1; приведет к записи в память, не назначенную pB, но может или не может быть допустимым пространством для записи.

1 голос
/ 04 ноября 2010

Как говорили многие здесь, вы нарушили так называемое Правило Одного Определения (ODR).

Важно понимать, как собираются программы на C / C ++. То есть единицы перевода (файлы cpp) компилируются отдельно , без какой-либо связи друг с другом. Следующий компоновщик собирает исполняемый файл в соответствии с символами и частями кода, сгенерированными компилятором. У него нет высокоуровневой информации о типе, поэтому он не может (и не должен) обнаруживать проблему.

Так что вы на самом деле обманули компилятор, избили себя, стреляйте ногой, что хотите.

Одна вещь, которую я хотел бы упомянуть, это то, что на самом деле правило ODR нарушается очень часто из-за незначительных изменений в различных заголовочных файлах и разных определениях, но обычно проблем нет, и люди даже не осознают этого.

Например, структура может иметь член типа LPCTSTR, который является указателем на char или wchar_t, в зависимости от определений, включений и т. Д. Но этот тип нарушения "почти нормальный" , Пока вы на самом деле не используете этот элемент в по-разному скомпилированных единицах перевода, проблем нет.

Есть также много других распространенных примеров. Некоторые возникают из-за реализованных в классе функций-членов (встроенных), которые фактически компилируются в разный код в разных единицах перевода (например, из-за разных опций компилятора для разных единиц перевода).

Однако обычно это нормально. В вашем случае, однако, структура памяти структуры изменилась. И тут у нас настоящая проблема.

...