Как «включить» заголовочный файл? - PullRequest
2 голосов
/ 16 января 2012

У меня есть класс в файле AType.h, и он реализован в AType.cpp.

# include "PrivateType.h"

class AType{
    private:
    int a, b, c;
    PrivateType varX;

    public:
    ...
};

Я хочу использовать класс AType в файле main.cpp, и мне нужно будет включитьAType.h, но я хочу избежать включения PrivateType.h в main.cpp.
Я не могу создать varX с помощью malloc / new.
main.cpp должен знать размер AType при компиляциивремя.

Текущее решение: (Это плохо)

1 - Создать программу для печати sizeof(AType).
2 - Изменить заголовок:

# ifdef ATYPE_CPP
    # include "PrivateType.h"
#endif

class AType{
    private:
    # ifdef ATYPE_CPP
        int a, b, c;
        PrivateType varX;
    # else
        char data[ the size that was printed ];
    # endif

    public:
    ...
};

3 - И AType.cpp начнется с:

# define ATYPE_CPP
# include "AType.h"

Редактировать 1

Есть ли способ или инструмент для автоматического изменениясложная структура на примитивные типы C?
Я не хочу открывать заголовочные файлы и находить структуры.

Если PrivateType равен:

struct DataType {
    float a, b;
};
class PrivateType {
    void* a;
    int b;
    short c;
    DataType x;

    ... functions
};

AType будет изменен на:

class AType {
    int a, b, c;
    struct x {
        void* a;
        int b;
        short c;
        struct x2{
            float a, b;
        };
    };
};

И я бы обрабатывал методы копирования / равенства отдельно.
Я использую GCC или Clang.

Редактировать 2
Новыйрешение?

Этодля GCC.

1 - получить sizeof(AType) и __alignof__(AType).
2 - изменить заголовок:

# ifdef ATYPE_CPP
    # include "PrivateType.h"
#endif

class AType{
    private:
    # ifdef ATYPE_CPP
        int a, b, c;
        PrivateType varX;
    # else
        char data[ 'the sizeof(AType)' ];
    # endif

    public:
    ...
}
# ifdef ATYPE_CPP
    ;
# else
    __attribute__ (( aligned( 'The __alignof__(AType)' ) ));
# endif

3 - записать все методы копирования / равенства в AType.cpp,

Будет ли это работать?

Ответы [ 5 ]

6 голосов
/ 16 января 2012

Ваш текущий метод потерпит неудачу тонко и катастрофически.Компилятор должен постоянно видеть одно и то же объявление для класса.В частности, рассмотрим сгенерированный компилятором оператор равенства или присваивания для класса AType без правильного определения PrivateType присутствует.Компилятор неправильно сгенерирует метод копирования / равенства для массива char.

Что вы можете сделать, это forward forward вашего личного типа:

class PrivateType;

class AType{
    private:
    int a, b, c;
    PrivateType *varX;

    public:
    ...
};

Замечаниечто varX теперь является указателем на класс, который еще не определен (конечно, вы должны распределить / освободить это самостоятельно; может помочь тип умного указателя).В вашем AType.cpp вы можете #include "PrivateType.h" получить полное определение, чтобы вы могли использовать членов класса.

5 голосов
/ 16 января 2012

Вы не можете делать то, что хотите (потому что вы исключили динамическое размещение), и ваше «решение» не работает вообще, даже если вы избегаете проблем с сгенерированными компилятором специальными функциями-членами, о которых упоминали другие.Одна проблема заключается в том, что типы имеют не только размер, но и выравнивание.Например, ваш реальный класс содержит int, однако ваш замещающий класс содержит только массив char.Теперь на большинстве платформ int имеет выравнивание 4 (то есть int должно быть расположено на границе 4 байта), в то время как char имеет выравнивание 1 (оно не может иметь никакого другого выравнивания без нарушения стандарта).То есть, как только вы пытаетесь создать объект с вашим определением замены, вы рискуете получить его смещение, что в лучшем случае приведет к значительному замедлению, в худшем случае к краху вашей программы (и в абсолютно худшем случае)., он будет работать в ваших тестах, но потерпит неудачу при фактическом использовании).

1 голос
/ 16 января 2012

Распространенным решением является Pimpl: создайте структуру / класс, который содержит члены открытого класса.Старый класс имеет только один член: указатель на эту новую структуру / класс.Обратите внимание:

struct ATypeData;

class AType
{
private:
    ATypeData *m_pData;

    public:
    ...
};

Исходный файл для AType будет иметь фактическое определение ATypeData, а также включать любые заголовки, необходимые для этих элементов.

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

0 голосов
/ 16 января 2012

Есть способ, но я бы не советовал это делать.В AType.cpp у вас есть какая-то карта контекста модуля компиляции от каждого объекта AType до PrivateType.

Вы также можете иметь некоторый пул объектов PrivateType, и тогда у вашего AType будет указатель (или дажевозможно ссылка) на который он имеет из пула.Конечно, это ограничивает количество объектов AType, которые вы можете иметь, числом в пуле.

Наконец, вы можете использовать размещение нового, если вы не считаете, что также запрещено использование нового.В таком случае у вас есть структура выше с памятью, а также указатель члена PrivateType *.В вашем конструкторе вы будете использовать размещение new для создания вашего объекта в области памяти, в вашем деструкторе вам придется вызывать деструктор вашего закрытого типа.Ваш член может быть ссылкой.

PrivateType & varX;

AType::AType : varX( *new(data)PrivateType )
{
}

~AType::Aype()
{
    varX.~PrivateType();
}

Этот тип моделирует то, что у вас есть сейчас, но использует новое слово "зло" ...

0 голосов
/ 16 января 2012

Нет способа делать то, что ты хочешь.Теперь такая вещь, как «uninclude».
Но, возможно, эти идеи могут быть полезны:

  1. Включить PrivateType.h, затем добавить определения препроцессора, которые не позволят его использовать.Что-то вроде #define PrivateType dont use this!.Если это будет сделано после того, как AType.h завершит все законное использование, эти определения не вызовут проблем.

  2. Как и в вашем решении, определите массив вместо PrivateType.Но:
    а.Определите его как массив void *.Это обеспечит правильное выравнивание массива (я предполагаю, что ни у одного типа нет более строгих требований к выравниванию, чем у указателя).
    b.Определите размер как простую константу.Не запускать программу дважды.
    c.В AType.c убедитесь, что размер соответствует реальному размеру.Один из способов - определить два бесполезных массива, один размером DEFINED_SIZE-sizeof(AType), а другой размером sizeof(AType)-DEFINED_SIZE.Если есть разница, компиляция не удастся из-за массива с отрицательным размером.
    d.Вам придется вручную изменить> e DEFINED_SIZE при изменении AType.Но вы получите хорошее напоминание, если забудете.

...