Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is std::unique_ptr reset not the same as assignment?

I am trying to understand why

std::unique_ptr<MyClass> p = new MyClass; 

Does not work, but

std::unique_ptr<MyClass> p;
p.reset(new MyClass);

is fine. I somewhat understand how they are different, but I would like to know why the choice was made to make them different. What is the danger in assignment not being the same as reset?

like image 895
starmole Avatar asked May 03 '18 03:05

starmole


People also ask

What does unique_ptr Reset do?

std::unique_ptr::reset Destroys the object currently managed by the unique_ptr (if any) and takes ownership of p. If p is a null pointer (such as a default-initialized pointer), the unique_ptr becomes empty, managing no object after the call.

Do we need to delete unique_ptr?

An explicit delete for a unique_ptr would be reset() . But do remember that unique_ptr are there so that you don't have to manage directly the memory they hold. That is, you should know that a unique_ptr will safely delete its underlying raw pointer once it goes out of scope.

Can unique_ptr be copied?

A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr , passed by value to a function, or used in any C++ Standard Library algorithm that requires copies to be made. A unique_ptr can only be moved.


2 Answers

Firstly, std::unique_ptr<MyClass> p = new MyClass; is not assignment, it is copy initialization. And it doesn't work because the constructor of std::unique taking a raw pointer is marked as explicit:

explicit unique_ptr( pointer p ) noexcept;

It is declared as explicit to avoid unexpected (might be dangerous) implicit conversions, eg:

void foo(std::unique_ptr<int> uptr);

int *rptr = new int;
foo(rptr); // suppose rptr is implicitly converted to std::unique_ptr<int>
           // then the ownership is passed to the parameter uptr

// when foo() returns uptr is destroyed; the pointer managed by it is deleted too
// since rptr has been deleted continue to deference on it leads to UB
*rptr = 42; // UB

Note that explicit constructors are not considered in copy initialization (eg std::unique_ptr<MyClass> p = new MyClass;). You can use them in direct initialization instead (eg std::unique_ptr<MyClass> p (new MyClass);). They are used to prohibit implicit conversions, but you can perform explicit conversions. Like the usage of reset, you have to do these things explicitly, to show (and make yourself) that you're pretty sure about what you're doing.

BTW: The assignment from raw pointer doesn't work either, because std::unique_ptr doesn't have an overloaded assignment operator taking a raw pointer as parameter. For the reason above, raw pointer can't be implicitly converted to std::unique_ptr, so the move assignment operator (which takes std::unique_ptr as parameter) won't be considered either.

like image 156
songyuanyao Avatar answered Oct 18 '22 17:10

songyuanyao


I am trying to understand why std::unique_ptr<MyClass> p = new MyClass; does not work

The same reason as @songyuanyao mentioned, where it's declared explicit, tells that you can still initialize it in a different form of initialization that surpasses explicit:

// Valid, since now it's 'explicit'
std::unique_ptr<MyClass> p { new MyClass{} };
like image 22
Dean Seo Avatar answered Oct 18 '22 19:10

Dean Seo