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

问题描述 投票:2回答:6

我在文件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中写入所有复制/相等方法。

会运作吗?

c++ c include header-files encapsulation
6个回答
5
投票

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


6
投票

您当前的方法将失败并带来灾难性的后果。编译器必须始终在该类中看到same声明。具体来说,考虑在没有正确定义AType的情况下,编译器为类PrivateType生成的相等或赋值运算符。编译器将错误地为char数组生成一个复制/相等方法。

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

class PrivateType;

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

    public:
    ...
};

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


1
投票

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

struct ATypeData;

class AType
{
private:
    ATypeData *m_pData;

    public:
    ...
};

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

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


0
投票

没有办法做你想做的。现在,诸如“取消包含”之类的东西。但是,也许这些想法可能有用:

  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。更改DEFINED_SIZE时,您必须手动更改AType。但是,如果您忘记了,将会得到很好的提醒。


0
投票

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

您还可以具有某种类型的PrivateType对象池,然后您的AType将具有一个指针(甚至可能是引用),该指针从该池中指向该指针。当然,它将您可以拥有的AType对象的数量限制为池中的数量。

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

PrivateType & varX;

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

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

这种排序模型可以模拟您现在所拥有的,但是会使用“邪恶的”新单词...


0
投票

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

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.