std::unique_ptr
有一个删除的拷贝构造函数,这意味着如果你的类unique_ptr
中有一个Foo
作为数据成员,那么你必须为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
上使用非标准包装)?
您的方法的问题是它正在改变unique_ptr的含义。 unique_ptr的关键在于它告诉谁是对象的所有者。如果为unique_ptr添加一个拷贝构造函数,那意味着什么?你在复制所有权吗? A和B都独一无二吗?那没有意义。如果他们共享所有权,那么您应该使用shared_ptr来指示共享所有权。如果您想拥有该对象副本的唯一所有者,您自然会通过make_unique(* pFoo)表示。使用基础对象和派生对象,使基础对象具有 virtual unique_ptr <Foo> Clone()const = 0; 是一个完全正常的结构。也就是说,派生类知道如何复制自己,因此它们不会生成切片副本,但是它们会向基类返回unique_ptr,以指示您将拥有它们生成的副本。在这些克隆操作中,是的,您将需要显式处理派生类的不可复制成员,因此您将无法仅使用默认或生成的复制构造函数。您需要回答“复制包含无法复制的内容的内容是什么意思?” 作为一个具体的例子,复制具有互斥锁的派生类意味着什么?如果它被锁定而另一个线程正在等待它怎么办?看看为什么很难给出一般答案?
这种方法很好,但是你应该非常小心,不要在你不打算时克隆你的对象。
同样从unique_ptr继承可能会提高性能
让我首先解释一下你想做什么:你想要Foo
的每个实例在Base
中都有自己的b
实例;特别是,如果你复制一个Foo
,副本将有自己的新Base
,最初具有相同的“值”。换句话说,Base
应该表现得像一个值。
同时,你不能将Base
直接存储在Foo
中,因为它是一个抽象类。换句话说,你想要b
是polymorphic
。
你有它:你想要一个多态值。其他人已经认识到这种需求,并建议将C ++ 20用作polymorphic_value<Base>
。从文档:
类模板polymorphic_value在自由存储分配的对象上赋予类似值的语义。 polymorphic_value可以保存从T公开派生的类的对象,并且复制polymorphic_value将复制派生类型的对象。
它有一个参考实现,您现在可以使用它。非常简单地说,它是围绕std::unique_ptr
的包装,类似于你的建议。