仅仅以避免隐式复制构造函数删除,使用shared_ptr而不是unique_ptr作为类成员明智

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

本文关键字:ptr unique 成员 shared 复制 删除 构造函数 使用      更新时间:2023-10-16

我想保留一个大型(但不是特别复杂*)的默认复制构造人,但理想情况下是想用smart-替换一些原始的指针成员指针替代。

unique_ptr似乎是此默认值,但它隐含地删除了我的类的复制构造函数。

shared_ptr而不是允许我保留类的复制构造函数。即使我不想真正地"共享"资源,这可能是一个简单地坚持共享_ptr的好理由。我真的只想保留随时可用的复制构造函数(烦人为整个班级编写手动复制构造函数,只是为了用unique_ptr替换指针),就像我仍然使用原始指针时所拥有的那样。

搜索何时使用shared_ptr vs. unique_ptr,我从未看到对复制构建器的简单保存,被指示为使用shared_ptr的可能关键原因(可能的例外https://stackoverflow.com/a/16030118/3673329,但是不给出任何细节),但我也没有直接看到这不能成为有效选择的任何理由。

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

*尤其是,只要我使用原始指针成员而不是智能的

如果使用std::shared_ptr的唯一原因是保留类的默认复制构造性,则优先考虑易于使用,而不是相对于资源处理的类的预期语义。我认为,您必须提前决定班级是否应共享其资源或独家拥有它。如果您不确定班级是否应该通过复制的实例共享其资源,那么设计中的其他内容至少可能是可疑的。

本指南可能有一个例外,即std::shared_ptr<const YourType>。对于仅读取访问的情况,使用这种技术允许默认复制构造可能是可以接受的(尽管在不同的上下文中,肖恩父母说std::shared_ptr<const T>可以接受以获取价值语义)。

请注意进一步的含义:如果您共享一个std::shared_ptr实例,您不仅可以共享Pointee的状态和功能,还可以共享寿命控制。如果后者不是您的意图,只需共享参考(优选)或指向Pointee的原始指针,例如通过类似Getter的成员功能。如果您的班级消费部分不知道Pointee是否还活着或已经被销毁,则可以选择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);
}