C++ 具有 unique_ptr 的类的复制构造函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16030081/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Copy constructor for a class with unique_ptr
提问by codefx
How do I implement a copy constructor for a class that has a unique_ptr
member variable? I am only considering C++11.
如何为具有unique_ptr
成员变量的类实现复制构造函数?我只考虑 C++11。
采纳答案by Daniel Frey
Since the unique_ptr
can not be shared, you need to either deep-copy its content or convert the unique_ptr
to a shared_ptr
.
由于unique_ptr
无法共享,您需要深复制其内容或将 转换unique_ptr
为shared_ptr
.
class A
{
std::unique_ptr< int > up_;
public:
A( int i ) : up_( new int( i ) ) {}
A( const A& a ) : up_( new int( *a.up_ ) ) {}
};
int main()
{
A a( 42 );
A b = a;
}
You can, as NPE mentioned, use a move-ctor instead of a copy-ctor but that would result in different semantics of your class. A move-ctor would need to make the member as moveable explicitly via std::move
:
正如 NPE 所提到的,您可以使用移动构造函数而不是复制构造函数,但这会导致类的语义不同。move-ctor 需要通过std::move
以下方式显式地使成员成为可移动的:
A( A&& a ) : up_( std::move( a.up_ ) ) {}
Having a complete set of the necessary operators also leads to
拥有一套完整的必要运算符也会导致
A& operator=( const A& a )
{
up_.reset( new int( *a.up_ ) );
return *this,
}
A& operator=( A&& a )
{
up_ = std::move( a.up_ );
return *this,
}
If you want to use your class in a std::vector
, you basically have to decide if the vector shall be the unique owner of an object, in which case it would be sufficient to make the class moveable, but not copyable. If you leave out the copy-ctor and copy-assignment, the compiler will guide your way on how to use a std::vector with move-only types.
如果你想在 a 中使用你的类std::vector
,你基本上必须决定向量是否应该是对象的唯一所有者,在这种情况下,它足以使类可移动,但不可复制。如果您省略 copy-ctor 和 copy-assignment,编译器将指导您如何使用带有仅移动类型的 std::vector。
回答by davidhigh
The usual case for one to have a unique_ptr
in a class is to be able to use inheritance (otherwise a plain object would often do as well, see RAII). For this case, there is no appropriate answer in this thread up to now.
unique_ptr
在类中拥有 a 的通常情况是能够使用继承(否则普通对象通常也会这样做,请参阅 RAII)。对于这种情况,到目前为止,该线程中没有合适的答案。
So, here is the starting point:
所以,这里是起点:
struct Base
{
//some stuff
};
struct Derived : public Base
{
//some stuff
};
struct Foo
{
std::unique_ptr<Base> ptr; //points to Derived or some other derived class
};
... and the goal is, as said, to make Foo
copiable.
......正如所说,目标是使可Foo
复制。
For this, one needs to do a deep copyof the contained pointer to ensure the derived class is copied correctly.
为此,需要对所包含的指针进行深度复制,以确保正确复制派生类。
This can be accomplished by adding the following code:
这可以通过添加以下代码来实现:
struct Base
{
//some stuff
auto clone() const { return std::unique_ptr<Base>(clone_impl()); }
protected:
virtual Base* clone_impl() const = 0;
};
struct Derived : public Base
{
//some stuff
protected:
virtual Derived* clone_impl() const override { return new Derived(*this); };
};
struct Foo
{
std::unique_ptr<Base> ptr; //points to Derived or some other derived class
//rule of five
~Foo() = default;
Foo(Foo const& other) : ptr(other.ptr->clone()) {}
Foo(Foo && other) = default;
Foo& operator=(Foo const& other) { ptr = other.ptr->clone(); return *this; }
Foo& operator=(Foo && other) = default;
};
There are basically two things going on here:
这里基本上有两件事:
The first is the addition of copy and move constructors, which are implicitly deleted in
Foo
as the copy constructor ofunique_ptr
is deleted. The move constructor can be added simply by= default
... which is just to let the compiler know that the usual move constructor shall notbe deleted (this works, asunique_ptr
already has a move constructor which can be used in this case).For the copy constructor of
Foo
, there is no similar mechanism as there is no copy constructor ofunique_ptr
. So, one has to construct a newunique_ptr
, fill it with a copy of the original pointee, and use it as member of the copied class.In case inheritance is involved, the copy of the original pointee must be done carefully. The reason is that doing a simple copy via
std::unique_ptr<Base>(*ptr)
in the code above would result in slicing, i.e., only the base component of the object gets copied, while the derived part is missing.To avoid this, the copy has to be done via the clone-pattern. The idea is to do the copy through a virtual function
clone_impl()
which returns aBase*
in the base class. In the derived class, however, it is extended via covariance to return aDerived*
, and this pointer points to a newly created copy of the derived class. The base class can then access this new object via the base class pointerBase*
, wrap it into aunique_ptr
, and return it via the actualclone()
function which is called from the outside.
第一个是添加复制和移动构造函数,它们在 in
Foo
的复制构造函数unique_ptr
被删除时被隐式删除。此举构造可以简单地通过添加= default
...这只是为了让编译器知道,通常的移动构造函数将不会被删除(此作品,unique_ptr
已经有一个移动构造函数可以在这种情况下使用)。对于 的复制构造函数
Foo
,没有类似的机制,因为没有 的复制构造函数unique_ptr
。因此,必须构造一个 newunique_ptr
,用原始指针的副本填充它,并将其用作复制类的成员。如果涉及继承,必须仔细复制原始指针。原因是
std::unique_ptr<Base>(*ptr)
在上面的代码中进行简单的复制会导致切片,即只复制对象的基本组件,而缺少派生部分。为避免这种情况,必须通过克隆模式进行复制。这个想法是通过
clone_impl()
一个Base*
在基类中返回 a的虚函数来进行复制。然而,在派生类中,它通过协方差扩展以返回 aDerived*
,并且 this 指针指向派生类的新创建的副本。然后基类可以通过基类指针访问这个新对象Base*
,将它包装成 aunique_ptr
,并通过clone()
从外部调用的实际函数返回它。
回答by Scott Langham
Try this helper to create deep copies, and cope when the source unique_ptr is null.
试试这个助手来创建深拷贝,并在源 unique_ptr 为空时处理。
template< class T >
std::unique_ptr<T> copy_unique(const std::unique_ptr<T>& source)
{
return source ? std::make_unique<T>(*source) : nullptr;
}
Eg:
例如:
class My
{
My( const My& rhs )
: member( copy_unique(rhs.member) )
{
}
// ... other methods
private:
std::unique_ptr<SomeType> member;
};
回答by StereoMatching
Daniel Frey mention about copy solution,I would talk about how to move the unique_ptr
Daniel Frey 提到复制解决方案,我会谈谈如何移动 unique_ptr
#include <memory>
class A
{
public:
A() : a_(new int(33)) {}
A(A &&data) : a_(std::move(data.a_))
{
}
A& operator=(A &&data)
{
a_ = std::move(data.a_);
return *this;
}
private:
std::unique_ptr<int> a_;
};
They are called move constructor and move assignment
它们被称为移动构造函数和移动赋值
you could use them like this
你可以像这样使用它们
int main()
{
A a;
A b(std::move(a)); //this will call move constructor, transfer the resource of a to b
A c;
a = std::move(c); //this will call move assignment, transfer the resource of c to a
}
You need to wrap a and c by std::move because they have a name std::move is telling the compiler to transform the value to rvalue reference whatever the parameters are In technical sense, std::move is analogy to something like "std::rvalue"
您需要用 std::move 包装 a 和 c 因为它们有一个名称 std::move 告诉编译器将值转换为右值引用,无论参数是什么在技术意义上,std::move 类似于“标准::右值”
After moving, the resource of the unique_ptr is transfer to another unique_ptr
移动后,unique_ptr的资源转移到另一个unique_ptr
There are many topics that document rvalue reference; this is a pretty easy one to begin with.
有许多主题记录了右值参考;这是一个很容易开始的。
Edit :
编辑 :
The moved object shall remain valid but unspecified state.
移动的对象应保持有效但未指定的状态。
C++ primer 5, ch13 also give a very good explanation about how to "move" the object
C++ 入门 5,ch13 也很好地解释了如何“移动”对象
回答by Splash
I suggest use make_unique
我建议使用 make_unique
class A
{
std::unique_ptr< int > up_;
public:
A( int i ) : up_(std::make_unique<int>(i)) {}
A( const A& a ) : up_(std::make_unique<int>(*a.up_)) {};
int main()
{
A a( 42 );
A b = a;
}
回答by IceFire
unique_ptr
is not copyable, it is only moveable.
unique_ptr
不可复制,只能移动。
This will directly affect Test, which is, in your second, example also only moveable and not copyable.
这将直接影响测试,即在您的第二个示例中,它也只能移动而不能复制。
In fact, it is good that you use unique_ptr
which protects you from a big mistake.
事实上,你使用它是好的,unique_ptr
它可以保护你免受大错误。
For example, the main issue with your first code is that the pointer is never deleted which is really, really bad. Say, you would fix this by:
例如,你的第一个代码的主要问题是指针永远不会被删除,这真的非常糟糕。说,你可以通过以下方式解决这个问题:
class Test
{
int* ptr; // writing this in one line is meh, not sure if even standard C++
Test() : ptr(new int(10)) {}
~Test() {delete ptr;}
};
int main()
{
Test o;
Test t = o;
}
This is also bad. What happens, if you copy Test
? There will be two classes that have a pointer that points to the same address.
这也不好。如果你复制会发生什么Test
?将有两个类具有指向相同地址的指针。
When one Test
is destroyed, it will also destroy the pointer. When your second Test
is destroyed, it will try to remove the memory behind the pointer, as well. But it has already been deleted and we will get some bad memory access runtime error (or undefined behavior if we are unlucky).
当一个Test
被销毁时,它也会销毁指针。当您的第二个Test
被销毁时,它也会尝试删除指针后面的内存。但是它已经被删除了,我们会得到一些不好的内存访问运行时错误(或者如果我们不走运,就会出现未定义的行为)。
So, the right way is to either implement copy constructor and copy assignment operator, so that the behavior is clear and we can create a copy.
所以,正确的方法是要么实现拷贝构造函数,要么实现拷贝赋值运算符,这样行为就清楚了,我们可以创建一个副本。
unique_ptr
is way ahead of us here. It has the semantic meaning: "I am unique
, so you cannot just copy me." So, it prevents us from the mistake of now implementing the operators at hand.
unique_ptr
在这里遥遥领先于我们。它具有语义:“我是unique
,所以你不能只是复制我。”所以,它可以防止我们现在实现手头的操作符的错误。
You can define copy constructor and copy assignment operator for special behavior and your code will work. But you are, rightfully so (!), forced to do that.
您可以为特殊行为定义复制构造函数和复制赋值运算符,并且您的代码将起作用。但是你,理所当然地(!),被迫这样做。
The moral of the story: always use unique_ptr
in these kind of situations.
故事的寓意:总是unique_ptr
在这种情况下使用。