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

时间:2019-02-19 12:07:05

标签: c++ shared-ptr copy-constructor unique-ptr

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

unique_ptr似乎是默认设置,但是它隐式删除了我的类的副本构造函数。

shared_ptr允许我保留该类的副本构造函数。即使我不真的希望“共享”资源,也可能只是坚持使用shared_ptr的好理由;我真的只想保留现成的复制构造函数(避免为整个类编写一个手动复制构造函数,只是用unique_ptr替换一个指针),就像我仍然使用原始指针时一样。

在搜索何时使用shared_ptr与unique_ptr时,我从未看到将复制构造函数的简单保存指示为使用shared_ptr的可能关键原因(可能的异常https://stackoverflow.com/a/16030118/3673329,但未提供任何细节),但是我也没有直接看到为什么这不是一个有效的选择。

我认为shared_ptr可能会占用更多资源,但是假设我的情况没有什么实际问题。

*特别是,只要我使用原始指针成员而不是智能指针成员,就可以将类的默认/浅表复制用于我的目的。

2 个答案:

答案 0 :(得分:2)

如果使用std::shared_ptr的唯一原因是保留类的默认副本可构造性,则在资源处理方面,应优先考虑类的预期语义的易用性。我认为,您必须预先决定班级将共享其资源还是完全拥有它。如果不确定该类是否应该与复制的实例共享其资源,则设计中的其他内容可能至少是可疑的。

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

请注意另外一个含义:如果您共享一个std::shared_ptr实例,那么您不仅将共享指针的状态和功能,而且还将共享生命周期控制。如果后者不是您想要的,则只需共享一个引用(首选)或指向该指针的原始指针即可访问,例如通过类似getter的成员函数。如果类的消耗部分不知道该指针是否还活着或已经被销毁,则可以选择std::weak_ptr

答案 1 :(得分:0)

使用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);
}