Copy Constructor For a Class With Unique_Ptr

Copy constructor for a class with unique_ptr

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.

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:

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.

Copy constructor for a class with unique_ptr to an abstract class as a member

You did not say whether you have control of the code for the abstract class and the classes derived from it. If you do, then the easiest way is to provide a pure virtual method Clone in the abstract class and implement it in derived classes. This method should handle creating the right copies. Unfortunately, because unique_ptr is not copyable you need to iterate through your vector and create copies by calling Clone.

Copy constructor for a class that has unique ptr of a Base class

If you need to copy polymorphically, you will need to provide that in the interface of the type you are holding. Add a clone virtual function to Base and use that to create a copy that you can store in the copied Foo.

Other alternatives include not copying (delete the copy constructor) or use reference semantics (copies refer to the same object: change unique_ptr for shared_ptr) but neither of those alternatives really provide copies.

Explicitly calling the copy constructor of an object inside unique_ptr

What I'd like to do is manually call the copy constructor of the impl class inside the unique_ptr

Here lies your error. As you are inside the (copy) constructor of potato, there's no impl object already constructed over which you'd have to "manually" invoke the copy constructor.

Just construct your new impl passing to it a reference to the original impl to copy.

potato::potato(const potato& p)
: _pimpl(std::make_unique<impl>(*p._pimpl) {
}

As for assignment, you can easily forward to the impl assignment operator:

potato &operator=(const potato &p) {
*_pimpl = *p._pimpl;
return *this;
}

unique_ptr assignment and copy constructor clarification

In RegisterWindow

std::unique_ptr<Window> m_main_window; // for reference.

void Engine::registerWindow(Window &&window)
{
m_main_window = std::move(std::unique_ptr<Window>(&window)); // NOT CORRECT!
// crash waiting to happen!
}

std::unique_ptr<> is a thin wrapper for pointers allocated with new. In other words delete m_main_window.get() will be called from Engine's destructor. In addition, it is terrible practice to keep a pointer to a value passed by reference, as there is no guarantee that the window object will live as long as Engine::m_main-window.

As pointed out by @Jarod42, you should consider receiving a std::unique_ptr as a parameter to RegisterWindow()

void Engine::registerWindow(std::unique_ptr<Window> wnd) 
{
m_main_window = std::move(wnd);
}

// call as
std::unique_ptr<Window> w(new Window);
engine.registerWindow(std::move(w));

This ensures that the caller understands that wnd must be allocated with new. And that Engine will take ownership of the pointer.

Unique_ptr in a class

Your copy constructor might look like

Human::Human(const Human& rhs) :
name_(rhs.name_),
cat_(rhs.cat_ ? std::make_unique<Cat>(*rhs.cat_) : std::nullptr)
{}

but getting rid of std::unique_ptr and having Cat by value (or std::optional<Cat>) would be simpler:

Human::Human(const Human&) = default;

If Cat is polymorphic, a clone method would be required:

Human::Human(const Human& rhs) :
name_(rhs.name_),
animal_(rhs.animal_ ? rhs.animal_->clone() : std::nullptr)
{}

Why am I allowed to copy unique_ptr?

In the return statement, if you return a local variable, the expression is treated as an rvalue, and thus automatically moved. It is thus similar to:

  return std::move(p);

It invokes the unique_ptr(unique_ptr&&) constructor.

In the main function, bar() produces a temporary, which is an rvalue, and is also properly moved into the p in main.



Related Topics



Leave a reply



Submit