隐藏库依赖关系,不让库用户看到。

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;
}

但是这需要我在 FooImpl 类中复制 Foo 类的接口。那么,在我的情况下使用 PIMPL 是否有些过度呢?谢谢。
1个回答

8

当将一个头文件从其他头文件中解耦时,有几种方法可以使用:

  1. If the used library makes a promise about how it declares its types, you may be able to forward declare the needed types in your header. Of course, this still means you can only refer to these types as pointers or in function signatures in the header but this may be good enough. For example, if the used library promises to have a class LibraryType that you need to use, you can do something like this:

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

    This may cut you the necessary slack to use the type without including its header and without jumping through a PImpl approach.

  2. If the library doesn't make a promise about how it declares it types you may use void* to refer to the corresponding types. Of course, this means that whenever you access the data in your implementation, you'll need to cast the void* to the appropriate type. Since the type is statically known, using static_cast<LibraryType*> is perfectly fine, i.e., there isn't any overhead due to the cast, but it is still relatively painful to do.

  3. The other alternative is, of course, to use the PImpl idiom. If you type provides any reasonably service, it will probably change the interface quite a bit and it shouldn't amount much to replicating the interface between the class itself and the privately declared type. Also, note that the private type is just a data container, i.e., it is reasonably to just make it a struct and have no protection to its accesses. The only real issue is that you need to make sure that the type's definition is visible at the point where the destructor is called. Using std::shared_ptr<T>(new T(/*...*)) arranges for this.

有效地说,这三种方法虽然采用略有不同的技术,但都实现了同样的效果:它们为您提供了一个不透明的句柄,可在头文件中使用,其定义仅为实现所知。这样,库的客户端就不需要包含相应的头文件。但是,除非在构建库时解析符号,否则客户端仍需要访问所使用的库。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接