Copy constructor for class with unique_ptr

How to implement copy constructor for a class with unique_ptr variable? I only consider C ++ 11.

+79
c ++ c ++ 11 unique-ptr
Apr 16 '13 at 6:21
source share
6 answers

Since unique_ptr cannot be shared, you need to either deep copy its contents or convert unique_ptr to 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 mentioned in NPE, use move-ctor instead of copy-ctor, but this will lead to different semantics of your class. The move-ctor would have to make the element moveable explicitly via std::move :

 A( A&& a ) : up_( std::move( a.up_ ) ) {} 

Having a complete set of 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 std::vector , you basically need to decide whether the vector should be the sole owner of the object, in which case it would be enough to make the class movable, but not copied. If you do not specify copy-ctor and copy-assignment, the compiler will determine how to use std :: vector with types just for moving.

+64
Apr 16 '13 at 6:24
source share

The usual case in order to have unique_ptr in the class is to be able to use inheritance (otherwise a regular object would also often use this, see RAII). There is still no suitable answer in this thread for this case.

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, as already mentioned, is to make Foo copyable.

To do this, make a deep copy of the contents of the pointer to make sure that the derived class is copied correctly.

This can be achieved 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, 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; }; 

There are basically two things going on here:

  • The first is the addition of copy and move constructors, which are implicitly deleted in Foo when the unique_ptr copy constructor is unique_ptr . A move constructor can be added simply using = default ... to make it clear to the compiler that a regular move constructor should not be removed (this works because unique_ptr already has a move constructor that can be used in this case).

    There is no similar mechanism for the Foo copy constructor, since there is no unique_ptr copy unique_ptr . Thus, you need to create a new unique_ptr , fill it with a copy of the original pointee, and use it as a member of the copied class.

  • In the case of inheritance, a copy of the original pointee must be made carefully. The reason is that doing simple copying with std::unique_ptr<Base>(*ptr) in the above code will lead to slicing, i.e. Only the base component of the object is copied, and the derived part is missing.

    To avoid this, a copy should be made through the clone template. The idea is to make a copy through the virtual function clone_impl() which returns Base* in the base class. However, in a derived class, it expands through covariance to return Derived* , and this pointer points to a newly created copy of the derived class. Then the base class can access this new object through the Base* base class pointer, wrap it in unique_ptr and return it through the actual clone() function, which is called from the outside.

+16
Apr 6 '17 at 18:47 on
source share

Try this deep copy helper and handle when source unique_ptr is null.

  template< class T > std::unique_ptr<T> copy_unique(const std::unique_ptr<T>& source) { return source ? std::make_unique<T>(*source) : nullptr; } 

For example:

 class My { My( const My& rhs ) : member( copy_unique(rhs.member) ) { } // ... other methods private: std::unique_ptr<SomeType> member; }; 
+10
Nov 24 '14 at 11:50
source share

Daniel Fry mentioned copy solution, I would like to talk about how to move 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_; }; 

These are called the move constructor and the move destination.

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 with std :: move because they have the name std :: move tells the compiler to convert the value to rvalue, regardless of the parameters. In the technical sense, std :: move is similar to "std :: rvalue"

After moving, the unique_ptr resource is transferred to another unique_ptr

There are many topics that document the rvalue reference; It's pretty easy to start with .

Edit:

The moved object must remain a valid, but undefined state .

C ++ primer 5, ch13 also gives a very good explanation of how to "move" an object

+5
Apr 16 '13 at 6:32
source share

I suggest using 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; } 
+1
Jul 25 '18 at 14:13
source share

unique_ptr not unique_ptr , it is only movable.

This will directly affect Test, which in the second example can also be movable and not copyable.

In fact, it’s good that you use unique_ptr which protects you from a big mistake.

For example, the main problem with your first code is that the pointer is never deleted, which is very, very bad. Say you would fix it:

 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 pointing to the same address.

When one Test destroyed, it will also destroy the pointer. When your second Test is destroyed, it will also try to delete the memory behind the pointer. But it has already been deleted, and we will get some error during memory access execution (or undefined behavior if we are out of luck).

Thus, the right way is to implement the copy constructor and copy assignment operator so that the behavior is clear and we can create a copy.

unique_ptr far ahead of us here. It makes sense: " I am unique , so you cannot just copy me." Thus, this prevents us from making mistakes when implementing operators at hand.

You can define a copy constructor and copy assignment operator for specific behavior, and your code will work. But you are rightfully (!) Forced to do so.

The moral of this story is: always use unique_ptr in situations like this.

0
Jan 22 '19 at 16:04
source share



All Articles