Auto-cloning unique_ptr

Auto-cloning unique_ptr

本文关键字:ptr unique Auto-cloning      更新时间:2023-10-16

std::unique_ptr有一个删除的复制构造函数,这意味着如果你在Foo类中有一个unique_ptr作为数据成员,那么你必须为Foo编写自己的复制构造函数并手动深度复制该成员(即使编译器生成的复制构造函数对所有其他成员都很好)。

为了能够以多态方式复制,可以使用clone()方法模式。假设我们的对象有如下的克隆方法:

class Base {
    virtual std::unique_ptr<Base> clone() = 0;
};

Foo现在看起来是这样的:

class Foo {
public:
    ...
    Foo(Foo const& other)
        : b(other.b->clone())
        , // init 10 more members that could otherwise be auto-copied just fine
          // with the automatically generated copy constructor
    {}
    ...
private:
    std::unique_ptr<Base> b;
    //10 more data members
};

现在,我找到了一种自动克隆Foo::b的方法,通过在unique_ptr上编写一个包装器,通过调用clone来定义复制构造函数和赋值。

template <typename T>
class auto_cloned_unique_ptr
{
private:
    std::unique_ptr<T> up;
public:
    // copy constructor
    auto_cloned_unique_ptr(auto_cloned_unique_ptr<T> const& other)
        : up(other.up->clone()) {}
    // copy assignment
    auto_cloned_unique_ptr<T>& operator =(auto_cloned_unique_ptr<T> const& other)
    {
        this->up = other.up->clone();
        return *this;
    }
    auto_cloned_unique_ptr(std::unique_ptr<T> _up)
        : up(std::move(_up)) {}
    // Delegate everything else to unique_ptr
    auto_cloned_unique_ptr(auto_cloned_unique_ptr<T>&& other)
        : up(std::move(other.up)) {}
    auto_cloned_unique_ptr<T>& operator =(auto_cloned_unique_ptr<T>&& other)
    {
        this->up = std::move(other.up);
        return *this;
    }
    auto operator *() const {return *up;}
    auto operator->() const {return up.operator->();}
    auto get() -> const {return up.get();}
};

现在,如果我们使用这个,我们不需要定义我们自己的复制构造函数:

class Foo2 {
public:
    ...
private:
    auto_cloned_unique_ptr<Base> b;
    //10 more data members
};

这样的方法是非常不赞成的(在unique_ptr上使用非标准包装器)吗?

让我先解释一下你想做什么:你想让Foo的每个实例在b中有自己的Base实例;特别是,如果复制一个Foo,该副本将有它自己的新Base,最初具有相同的"值"。换句话说,Base应该像一个

同时,你不能将Base直接存储在Foo中,因为它是一个抽象类。换句话说,您希望b成为polymorphic

你知道了:你想要一个多态值。其他人已经认识到这一需求,并将c++ 20命名为polymorphic_value<Base>。来自文档:

类模板,polymorphic_value,赋予类似值的语义自由存储分配的对象。一个多态值可以保存一个从T公开派生的类的多态值将复制派生类型的对象。

它有一个你现在可以使用的参考实现。简单地说,它是一个围绕std::unique_ptr的包装器,类似于您所建议的。

您的方法的问题在于它正在改变unique_ptr的含义。unique_ptr的关键在于它告诉谁是对象的所有者。如果为unique_ptr添加复制构造函数,这意味着什么?你是否在复制所有权?A和B都唯一地拥有这个东西?这没有道理。如果它们共享所有权,那么您应该使用shared_ptr来指示共享所有权。如果您想要对象副本的唯一所有者,您自然会通过make_unique(*pFoo)来表示。对于基类和派生类对象,让基类对象具有
virtual unique_ptr Clone() const=0;
是一个非常正常的结构。也就是说,派生类知道如何复制它们自己,所以它们不生成切片副本,但是它们向基类返回一个unique_ptr,以表明您将拥有它们生成的副本。在这些克隆操作中,是的,您需要显式地处理派生类的不可复制成员,因此您不能只使用默认的或生成的复制构造函数。你需要回答"复制包含不可复制内容的东西是什么意思"
作为一个具体的例子,复制具有互斥锁的派生类意味着什么?如果它被锁定,而另一个线程正在等待它呢?明白为什么很难给出一个笼统的答案了吧?

这种方法很好,但是您应该非常小心,不要在不打算克隆对象的情况下克隆对象。

继承unique_ptr也可以提高性能