使用 shared_ptr 而不是 unique_ptr 作为 class 成员只是为了避免隐式复制构造函数删除是否明智?

Is it sensible to use shared_ptr instead of unique_ptr as class member just to avoid implicit copy constructor deletion?

我想保留大型(但不是特别复杂*)的默认复制构造函数class,但理想情况下,我想用智能指针替代方案替换一些原始指针成员.

unique_ptr 似乎是默认设置,但它隐式删除了我的 class.

的复制构造函数

shared_ptr 而不是让我保留 class' 复制构造函数。这可能是坚持 shared_ptr 的一个很好的理由,即使我真的不想 'share' 资源;我真的只想保留现成的复制构造函数(为整个 class 编写手动复制构造函数很烦人,只是用 unique_ptr 替换指针),就像我仍然拥有它一样使用原始指针。

搜索何时使用 shared_ptr 与 unique_ptr,我从来没有看到复制构造函数的简单保存被指示为使用 shared_ptr 的可能关键原因(可能的例外 但没有提供任何细节),但我也没有直接看出这不是一个有效选择的任何原因。

我认为 shared_ptr 可能会占用更多资源,但假设我的情况不是真正的问题。

*特别是,class 的 default/shallow 复制对我来说很好,只要我使用原始指针成员而不是智能指针成员。

如果使用 std::shared_ptr 的唯一原因是保留 class 的默认复制可构造性,那么在资源方面,您将易用性置于 class 的预期语义之上处理。在我看来,你必须预先决定 class 是共享它的资源还是独占它。如果您不确定 class 是否应该与复制的实例共享其资源,设计中的其他内容至少可能是可疑的。

这条准则可能有一个例外,那就是 std::shared_ptr<const YourType>。在只读访问的情况下,使用这种技术来允许默认副本构造可能是可以接受的(尽管在不同的上下文中,here 是 Sean Parent 说 std::shared_ptr<const T> 是可以接受的值语义)。

注意一个进一步的含义:如果你共享一个 std::shared_ptr 实例,你不仅共享指针对象的状态和功能,你还共享生命周期控制。如果后者不是您想要的,只需共享一个引用(最好)或一个指向指针的原始指针,并具有访问权限,例如通过一个类似于 getter 的成员函数。如果你的 class 的消耗部分无法知道指针对象是否还活着或已经被摧毁,那么 std::weak_ptr 可能是一个选择。

使用 unique_ptr 并手动提供缺少的复制操作仍然有很多优势。

您可以获得正确性保证以及用于内部对象克隆的简单自定义点。

示例:

#include <memory>


struct LargeImplObject
{
    int x[1000];
};

struct CopyableHandle
{
    using impl_class = LargeImplObject;
    using implementation_type = std::unique_ptr<impl_class>;

    CopyableHandle()
    : impl_(construct())
    {}

    CopyableHandle(CopyableHandle&&) noexcept = default;
    CopyableHandle& operator=(CopyableHandle&&) noexcept = default;

    CopyableHandle(CopyableHandle const& other)
    : impl_(other.clone())
    {}

    CopyableHandle& operator=(CopyableHandle const& other)
    {
        impl_ = other.clone();
        return *this;
    }

    // note: no destructor

private:

    // customisation points
    static auto construct() -> implementation_type
    {
        return std::make_unique<impl_class>();
    }

    auto clone() const -> implementation_type
    {
        return std::make_unique<impl_class>(*impl_);
    }


    implementation_type impl_;
};

int main()
{
    auto a = CopyableHandle();
    auto b = a;
    auto c = std::move(a);
    a = std::move(c);
}