【问题标题】:PIMPL idiom and copy semanticPIMPL 成语和复制语义
【发布时间】:2016-01-27 10:30:53
【问题描述】:

我正在使用一个库,其中包含许多使用 PIMPL 习语构建的类。在我看来,我发现不好的是这些类是使用std::shared_ptr 实现的。这意味着对象实际上是“隐式共享的”。我的问题是:这是实现 PIMPL 的正确方法吗?或者 PIMPL 和“隐式共享”是两个不同的习语,因此默认情况下不应混用?处理复制语义的正确方法是什么?

【问题讨论】:

    标签: c++ deep-copy pimpl-idiom


    【解决方案1】:

    IMO,实现 pimpl 的正确方法是使用std::unique_ptr。 它更高效,并且 pimpl 对象应该由可见类唯一拥有,而不是共享(并且您不必为复制语义而烦恼)。

    PIMPL 和“隐式共享”确实是两个不同的习语。

    如果您仍然必须为 pimpl 使用 std::shared_ptr,那么您将必须显式定义复制分配操作(因为编译器将无法生成正确的操作)。

    【讨论】:

    • 我同意,我发现 'std::unique_ptr' 比 'std::shared_ptr' 实现更好。但有时在编写库时,您也需要隐藏小型可复制对象的实现,即 lib::Point3d 或 lib::UnicodeString。我的想法是,如果我正确理解了创建 PIMPL 习语的目的,那么没有标准的智能指针可以完成正确的工作。
    【解决方案2】:

    正如 coincoin 所说,使用 unique_ptr 而不是 shared_ptr。如果你想让你的类可复制,你总是可以在可见类的复制构造函数中调用 pimpl 对象的复制构造函数。

    class A {
        class impl;
        std::unique_ptr<impl> pimpl;
    public:
        A(int x);
        A(A const& old);
        ...
    };
    

    实施:

    class A::impl {
        int x;
    public:
        impl(int x)
            : x(x)  {}
        impl(impl const& old)
            : x(old.x) {}
    };
    
    A::A(int x)
        : pimpl(std::make_unique<impl>(x)) {}
    
    A::A(A const& old)
        : pimpl(std::make_unique<impl>(*old.pimpl)) {}
    

    【讨论】:

    • 不错,但是对于每个类,您都必须定义复制构造函数和复制赋值运算符(可能还有移动运算符?)。如果我们同意 PIMPL 不应该干扰类的复制语义(这实际上是问题所在),我建议特定的智能指针是最好的解决方案。
    • 如果你有足够多的 pimpl'ed 类,这可能是一个可行的选择。制作一个在所有情况下(尤其是异常情况)都表现良好的智能指针类确实需要一些思考。如果只有几个 pimpl'ed 类,那么只使用 unique_ptr(并且可能定义为 = default 的复制构造函数)可能是更简单的解决方案。
    • std::unique_ptr 已删除复制构造函数和运算符,所以我猜它不允许在拥有类中使用 =default 定义。一旦您对成员使用 std::unique_ptr ,整个类就变得不可复制,这是这种方法的巨大限制。但是,再一次,它看起来仍然比 std::shared_ptr 方法更安全。
    • 哦,我的意思是在 pimpl 类上复制默认构造函数。在我的示例中,我手动实现了它,但我想明确说明可以在那里节省大量工作。
    猜你喜欢
    • 2020-09-05
    • 2010-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    • 2017-11-12
    • 2010-09-21
    相关资源
    最近更新 更多