用unique_ptr复制类的构造函数
如何为具有unique_ptr
成员variables的类实现复制构造函数? 我只考虑C ++ 11。
由于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; }
你可以像NPE提到的那样使用移动代替copy-ctor,但这会导致你的类的语义不同。 move-ctor将需要通过std::move
将该成员显式std::move
:
A( A&& a ) : up_( std::move( a.up_ ) ) {}
有一套完整的必要的操作员也导致
A& operator=( const A& a ) { up_.reset( new int( *a.up_ ) ); return *this, } A& operator=( A&& a ) { up_ = std::move( a.up_ ); return *this, }
如果你想在std::vector
使用你的类,你基本上必须决定这个向量是否是一个对象的唯一所有者,在这种情况下,这个类是可移动的,但不能被复制。 如果你省去了copy-ctor和copy-assignment,编译器会引导你如何使用一个std :: vector和move-onlytypes。
尝试使用此帮助程序创build深层副本,并在源unique_ptr为null时应付。
template< class T > std::unique_ptr<T> copy_unique(const std::unique_ptr<T>& source) { return source ? std::make_unique<T>(*source) : nullptr; }
例如:
class My { My( const My& rhs ) : member( copy_unique(rhs.member) ) { } // ... other methods private: std::unique_ptr<SomeType> member; };
Daniel Frey提到了复制解决scheme,我会谈谈如何移动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_; };
他们被称为移动构造函数和移动任务
你可以像这样使用它们
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 }
你需要通过std :: move来包装a和c,因为他们有一个名字std :: move正在告诉编译器将这个值转换为rvalue引用,不pipe参数是什么在技术上,std :: move类似于“的std ::右值”
移动之后,unique_ptr的资源被转移到另一个unique_ptr
文档右值引用有很多题目, 这是一个非常容易的开始 。
编辑:
移动的对象应保持有效但未指定的状态 。
C ++ primer 5,ch13也对如何“移动”对象给出了很好的解释
在类中有一个unique_ptr
的通常情况是能够使用inheritance(否则一个普通的对象也可以,请参阅RAII)。 对于这种情况, 到现在为止还没有合适的答案 。
所以,这里是出发点:
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 };
…正如所说的,目标是让Foo
交付。
为此,需要对包含的指针进行深层拷贝 ,以确保派生类被正确地复制。
这可以通过添加以下代码来完成:
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, but a user-defined dtor is not necessary due to unique_ptr 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; };
基本上有两件事情在这里进行:
-
首先是复制和移动构造函数的添加,这些构造函数在
Foo
被隐式删除,因为unique_ptr
的复制构造函数被删除。 移动构造函数可以简单地通过= default
…来添加,这只是为了让编译器知道通常的移动构造函数不会被删除(这是unique_ptr
,因为unique_ptr
已经有一个可以在这种情况下使用的移动构造函数)。对于
Foo
的拷贝构造函数,没有类似的机制,因为没有unique_ptr
拷贝构造函数。 因此,必须构造一个新的unique_ptr
,用原始指针的副本填充它,并将其用作复制类的成员。 -
在涉及inheritance的情况下,原始指针的副本必须小心。 原因是在上面的代码中通过
std::unique_ptr<Base>(*ptr)
做一个简单的复制将导致切片,也就是说,只有对象的基本组件被复制,而派生部分丢失。为了避免这种情况,副本必须通过克隆模式完成。 这个想法是通过一个虚函数
clone_impl()
来进行复制,该函数在基类中返回一个Base*
。 然而,在派生类中,它通过协方差扩展来返回Derived*
,并且这个指针指向派生类的新创build副本。 然后,基类可以通过基类指针Base*
访问这个新对象,将其包装成一个unique_ptr
,并通过从外部调用的实际clone()
函数返回它。