考虑我正在写一个静态库。让它有一个班级Foo
// mylib.h
#include <dependency_header_from_other_static_library.h>
class Foo {
// ...
private:
type_from_dependent_library x;
}
正如您所看到的,这个库(让我们称之为mylib
)取决于另一个库。它汇编得很好。但是当用户编译它的代码(使用Foo
并包含mylib.h
)并链接到我的lib时,编译失败,因为用户需要有dependency_header_from_other_static_library.h
头文件来编译代码。
我想隐藏用户的这种依赖关系。怎么做到这一点?我想到的一件事是PIMPL
成语。像:
// mylib.h
#include <dependency_header_from_other_static_library.h>
class Foo {
// ...
private:
class FooImpl;
boost::shared_ptr<FooImpl> impl_;
}
// mylib_priv.h
class FooImpl {
// ...
private:
type_from_dependent_library x;
}
但它要求我复制Foo
中类FooImpl
的界面。而且,在我的案例中使用PIMPL
是否有点过分了?
感谢。
答案 0 :(得分:6)
将标头与其他标头分离时,您可以使用以下几种方法:
如果使用的库对如何声明其类型做出承诺,您可以转发在标头中声明所需的类型。当然,这仍然意味着您只能在标题中将这些类型称为指针或函数签名,但这可能已经足够了。例如,如果使用过的库承诺需要使用class LibraryType
,则可以执行以下操作:
// Foo.h
class LibraryType;
class Foo {
// ...
LibraryType* data;
};
这可能会减少使用该类型所需的冗余,而不包括其标题,也不会跳过PImpl方法。
如果库没有声明它如何声明它的类型,您可以使用void*
来引用相应的类型。当然,这意味着无论何时访问实现中的数据,都需要将void*
强制转换为适当的类型。由于该类型是静态知道的,因此使用static_cast<LibraryType*>
完全没问题,即由于演员阵容没有任何开销,但仍然相对痛苦。
另一种选择当然是使用PImpl习语。如果你键入提供任何合理的服务,它可能会相当多地更改接口,并且它不应该复制类本身和私有声明类型之间的接口。另外,请注意私有类型只是一个数据容器,即合理地只使它成为struct
并且对其访问没有保护。唯一真正的问题是你需要确保类型的定义在调用析构函数的位置是可见的。使用std::shared_ptr<T>(new T(/*...*))
安排此操作。
实际上,这三种方法都做了同样的事情,虽然技术略有不同:它们为您提供了一个不透明的句柄,可以在头文件中使用,其定义仅为实现所知。这样,库的客户端不需要包含相应的头文件。但是,除非在构建库时解析符号,否则客户端仍然需要访问已使用的库。