用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()函数返回它。