我在文件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中编写所有复制/相等方法。
它会起作用吗?
答案 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)
没有办法做你想做的事。现在就像“不包括”一样 但是,也许这些想法可能有用:
包含PrivateType.h
,然后添加会阻止使用它的预处理程序定义。像#define PrivateType dont use this!
这样的东西。如果在AType.h
完成所有合法用途后执行此操作,则这些定义不会造成任何问题。
与您的解决方案类似,定义一个数组而不是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
对象-这是一件好事。