4

考虑我正在编写一个静态库。让它有一个类Foo

// mylib.h
#include <dependency_header_from_other_static_library.h>

class Foo {
    // ...
private:
    type_from_dependent_library x;
}

如您所见,这个库(让我们称之为mylib)依赖于另一个库。它编译得很好。但是当用户编译它的代码(使用Foo和包含mylib.h)并与我的库链接时,编译失败,因为用户还需要有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;
}

但这需要我FooFooImpl. 而且,PIMPL在我的情况下使用它是否有点矫枉过正?

谢谢。

4

1 回答 1

8

将标头与其他标头分离时,您可以使用以下几种方法:

  1. 如果使用的库对如何声明其类型做出承诺,您可以在标头中转发声明所需的类型。当然,这仍然意味着您只能将这些类型称为指针或标头中的函数签名,但这可能已经足够了。例如,如果使用的库承诺有一个class LibraryType你需要使用的,你可以这样做:

    // Foo.h
    class LibraryType;
    class Foo {
        // ...
        LibraryType* data;
    };
    

    这可能会减少您使用该类型所需的时间,而无需包含其标题,也无需跳过 PImpl 方法。

  2. 如果库没有就它如何声明它的类型做出承诺,您可以使用它void*来引用相应的类型。当然,这意味着无论何时访问实现中的数据,都需要将void*转换为适当的类型。由于类型是静态已知的,因此使用static_cast<LibraryType*>非常好,也就是说,没有任何由于强制转换而产生的开销,但这样做仍然相对痛苦。

  3. 当然,另一种选择是使用 PImpl 成语。如果你的类型提供了任何合理的服务,它可能会改变接口相当多,它不应该在类本身和私有声明的类型之间复制接口。另外,请注意,私有类型只是一个数据容器,也就是说,将其设置为 astruct并且对其访问没有保护是合理的。唯一真正的问题是您需要确保类型的定义在调用析构函数时可见。使用std::shared_ptr<T>(new T(/*...*))为此安排。

实际上,所有三种方法都做同样的事情,尽管技术略有不同:它们为您提供了一个不透明的句柄,以便在头文件中使用,其定义只有实现才知道。这样库的客户端就不需要包含相应的头文件了。但是,除非在构建库时解析符号,否则客户端仍然需要访问使用的库。

于 2012-10-27T23:42:16.183 回答