如何“取消包含”头文件?

时间:2012-01-16 18:16:10

标签: c++ c include header-files encapsulation

我在文件AType.h中有一个类,它在AType.cpp中实现。

# include "PrivateType.h"

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

    public:
    ...
};

我想在文件main.cpp中使用类AType,我需要包含AType.h,但我想避免在main.cpp中包含PrivateType.h。 /> 我无法使用malloc / new创建varX 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中编写所有复制/相等方法。

它会起作用吗?

6 个答案:

答案 0 :(得分:6)

您当前的方法将会巧妙而且灾难性地失败。编译器必须始终查看该类的相同声明。具体来说,考虑编译器生成的类AType的相等或赋值运算符,而不存在PrivateType的正确定义。编译器会错误地为char数组生成复制/相等方法。

您可以做的是转发声明您的私人类型:

class PrivateType;

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

    public:
    ...
};

请注意,varX现在是一个尚未定义的类的指针(当然,您必须自己分配/解除分配;智能指针类型可能有帮助)。在AType.cpp中,您可以#include "PrivateType.h"获取完整定义,以便您可以实际使用该类的成员。

答案 1 :(得分:5)

你不能做你想要的(因为你排除了动态分配),并且你的“解决方案”一般不起作用,即使你避免了编译器生成的特殊成员函数的问题,而其他人提到过。一个问题是类型不仅具有大小,而且还具有对齐。例如,您的真实类包含int,但您的替换类仅包含char数组。现在在大多数平台上,int具有对齐4(即int必须位于4字节边界处),而char具有对齐1(它不能有任何其他对齐而不违反标准)。也就是说,一旦你尝试用你的替换定义创建一个对象,你就有可能使它不对齐,这在最好的情况下会导致大幅减速,在最坏的情况下你的程序崩溃(在绝对最坏的情况下) ,它会在您的测试中起作用,但在实际使用时会失败。)

答案 2 :(得分:1)

常见的解决方案是Pimpl:创建一个包含公共类成员的结构/类。旧类只有一个成员:指向这个新结构/类的指针。观察:

struct ATypeData;

class AType
{
private:
    ATypeData *m_pData;

    public:
    ...
};

AType的源文件将具有ATypeData的实际定义,以及包含这些成员所需的任何标头。

建议m_pData成员使用智能指针,这样就不必拥有析构函数和复制构造函数以及所有好东西。

答案 3 :(得分:0)

没有办法做你想做的事。现在就像“不包括”一样 但是,也许这些想法可能有用:

  1. 包含PrivateType.h,然后添加会阻止使用它的预处理程序定义。像#define PrivateType dont use this!这样的东西。如果在AType.h完成所有合法用途后执行此操作,则这些定义不会造成任何问题。

  2. 与您的解决方案类似,定义一个数组而不是PrivateType。但是:
    一个。将其定义为void *的数组。这将确保数组正确对齐(我假设没有类型具有比指针更严格的对齐要求)。
    湾将大小定义为简单常量。没有两次运行程序 C。在AType.c中,验证大小是否与实际大小相匹配。一种方法是定义两个无用的数组,一个大小为DEFINED_SIZE-sizeof(AType),另一个大小为sizeof(AType)-DEFINED_SIZE。如果存在差异,编译将因为具有负大小的数组而失败 d。更改DEFINED_SIZE时,您必须手动更改> e AType。但如果你忘了,你会得到一个很好的提醒。

答案 4 :(得分:0)

有一种方法,但我不建议这样做。在AType.cpp中,您可以使用从每个AType对象到PrivateType的某种编译单元范围映射。

你也可以拥有某种PrivateType对象池,然后你的AType会有一个指针(甚至可能是一个引用),它从池中拥有它。当然,它会限制您可以拥有的AType对象的数量。

最后你的选择是使用placement-new,如果你不认为这也是禁止使用new。在这种情况下,你的结构上面有内存,还有一个PrivateType *成员指针。在构造函数中,您将使用placement new在内存空间中创建对象,在析构函数中,您必须调用私有类型的析构函数。您的会员可以作为参考。

PrivateType & varX;

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

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

这种模式你现在拥有但使用“邪恶”的新词......

答案 5 :(得分:0)

一个类或一个库创建一堆除了lib本身以外的其他地方都不会使用的类型是很普遍的。特别是,您不希望通过malloc / new等单独创建它们。

一个理想的解决方案是当前C ++标准无法实现的,那就是在全局范围内将类型指定为private。当C ++ 20引入模块可能是一种方法。

今天通常要做的是指定一个特殊的名称空间(通常为namespace detail)并将所有私有类型放在此处。一条不成文的规则是,标头/ lib的用户不得直接使用detail名称空间中的任何内容。例如detail::PrivateType* foo = ...被禁止。

同时,这不会阻止您访问类/库已经公开提供的detail::PrivateType对象-这是一件好事。