我想避免重新编译包含公共头文件的所有内容,只是因为在类定义的私有部分中发生了某些变化。我正在调查PIMPL旁边的其他选项。
这就是我的尝试:
我创建了一个包含A类的库:
A_p.h 包含A类的私人部分
void PrivateMethod(int i);
A.h 公共标头文件:
class A
{
public:
A();
virtual ~A();
// other public members
private:
#ifdef A_PRIVATE
#include "A_p.h"
#endif
};
A.cpp
#define A_PRIVATE
#include "A.h"
A::A() {}
A::~A() {}
void A::PrivateMethod(int i) { }
然后我创建了一个Win32控制台项目,其中包含公共标题(A.h)和.lib文件的链接。
一切似乎都有效,但我想知道沿途的任何陷阱。任何人都可以详细说明这个吗?
答案 0 :(得分:6)
“一切似乎都有效” - 似乎有必要。您只是遇到了未定义的行为。这是一个不合格的程序 - 在使用该类的编译单元中,类定义必须相同。
由于这是UB,它似乎可行,但尝试在私有部分声明virtual
方法,您很可能会遇到一些明显的问题。
答案 1 :(得分:1)
抽象类允许您通过子类化抽象类来声明公共接口但具有私有数据和函数。
这不符合您描述的方式,因此在C ++标准中不受支持的一个关键原因是您提出的公开声明使得无法知道A
的大小。公开声明没有透露私人数据需要多少空间。因此,仅查看公共声明的代码无法执行new A
,无法为A
的数组定义分配空间,也无法使用指向A
的指针进行算术运算。
还有其他问题,可能会以某种方式解决,例如指向虚拟功能成员的指针。然而,这会导致不必要的并发症。
要创建抽象类,请在类中声明至少一个虚函数。使用= 0
而不是函数体定义虚函数。这表示它没有实现,因此除了作为从它派生的类的子对象之外,不能有抽象类的对象。
然后,在单独的私有代码中,您声明并定义派生自B
的类A
。您将需要提供创建和销毁对象的方法,可能使用公共“new”函数返回指向A
的指针,并通过调用可以看到B
的声明的私有函数来工作。公共“删除”函数,它接受指向A
的指针,并通过调用可以看到B
声明的私有函数来工作。
答案 2 :(得分:1)
有三种隐藏此类信息的好方法:
只有转发声明您的班级。这只有在它通过客户端代码(通过指针和/或引用)传递时才有效,并且只有在库中使用。您的库需要提供工厂函数或类似工具才能首先返回指针/引用,客户端永远不能调用new
或delete
公开抽象基类,并再次提供工厂函数(实例化只在库中可见的具体派生类)
使用 pimpl
我也同意你应该重新考虑任何如此庞大的课程,以至于隐藏它是必要的,但如果你真的无法解决它,那么这些都是你的选择。
关于如何&为什么ODR违规在实践中会破坏: