6

我通常将 boost::scoped_ptr 用于 pimpl(原因之一是因为如果我忘记处理复制构造函数,我不会感到惊讶)

但是,对于模板,我不能只将析构函数放在完全定义 impl 的 cpp 文件中,以满足 scoped_ptr 析构函数的要求。无论如何它确实有效,但我不确定它是保证有效还是偶然。是否有一些“最佳实践”或标准?scoped_ptr 是不可复制类中 pimpls 的最佳智能指针吗?

template <class T> class C {
public:
    C(){}
    ~C(){}
private:
    boost::scoped_ptr<T> pimpl_;
};
4

3 回答 3

13

碰巧 Herb Sutter 时隔很久又开始写他的 GotWs。第一个新的与“编译防火墙”有关。

你可能想看看:

GotW #100:编译防火墙(难度:6/10)

GotW #101:编译防火墙,第 2 部分(难度:8/10)

于 2011-12-05T14:36:20.303 回答
2

两年后,我更好地理解了这种情况,为了保持堆栈溢出答案的相关性和最新性,我今天将如何回答这个问题。

我原来问题的前提有些缺陷。使用 pimpl-idiom 的原因是对编译器隐藏实现细节。这是通过通过不透明指针(指向已声明但未定义的数据类型的指针)存储实现来完成的。这可以大大减少与类交互的其他编译单元所需的头文件数量,从而加快编译时间。在我的问题中的模板的情况下,需要在实例化点完全知道类型 T,这实际上需要在任何C<ImplType>使用的地方完全定义 impl 的类型,这显然不是 pimpl-idiom 的示例这个词的经典意义。

通过私有指针保存类数据还有其他原因,例如,它允许轻松实现无抛出移动和交换,并且如果您的类需要满足强大的异常保证也很好(请参阅复制和交换习语What is复制和交换成语?)。另一方面,它在每次访问 impl 时添加了一层间接层(通常导致缓存未命中),并在创建和销毁 impl 时添加堆分配/释放。这些可能是严重的性能损失,因此不应将此解决方案视为灵丹妙药。

如果可以使用 C++11,则应使用 std::unique_ptr 而不是 boost::scoped_ptr。

于 2014-06-11T12:30:21.670 回答
1

boost::shared_ptr除了在实例化点之外不需要完整的定义——在构造函数中,在 pimpl 的情况下。 boost::shared_ptr然而,它适合 pimpl 成语,因为它给出了非常意想不到的语义(赋值或复制的引用语义);如果您真的想要增加智能指针的复杂性,boost::scoped_ptr那将更合适(但它确实需要在其析构函数被实例化时进行完整定义)。

对于模板,使用 pimpl 习惯用法作为标头中的实现细节是没有意义的。在没有 的情况下export,类模板的所有实现细节都必须包含在使用模板的任何地方,因此 pimpl 惯用语背后的动机不复存在。

于 2011-12-05T14:40:54.800 回答