I am trying to understand how unique pointers work in modern C .
When I went through the documentations (cppreference and others), I was able to understand that unique_ptr
will transfer ownership and not share it. But I am not able to understand why unique_ptr
is acting strange when working with a raw pointer passed into its constructor.
For example:
#include <iostream>
#include <memory>
class foo{
int x;
public:
foo(): x(0) {}
foo(int a): x(a) {}
foo(const foo& f): x(f.x) {}
};
int main(){
foo *ptr = new foo(5);
std::unique_ptr<foo> uptr(ptr);
std::cout << ptr << "\n";
std::cout << uptr.get() << "\n";
return 0;
}
Output below:
0x5c7bc80
0x5c7bc80
Queries:
- Is the raw pointer being passed into the copy constructor? Isn't the copy constructor deleted (
=delete
)? Why is the raw pointer printing the same address asunique_ptr
? - Is this a design flaw of
unique_ptr
? - How do I overcome this issue?
- What is the use of
std::make_unique()
? I tried changing the linestd::unique_ptr<foo> uptr(ptr);
tostd::unique_ptr<foo> uptr(std::make_unique<foo>(*ptr));
but nothing changed.
CodePudding user response:
Is the raw pointer being passed into the copy constructor?
Not the copy constructor, no (that takes another unique_ptr
as input). The raw pointer is being passed to a converting constructor instead, specifically this one in this case:
explicit unique_ptr( pointer p ) noexcept;
Isn't the copy constructor deleted (
=delete
)?
Yes. But this code is not invoking the copy constructor.
Why is the raw pointer printing the same address as the
unique_ptr
?
Because the unique_ptr
is simply copying the input pointer as-is into its own internal member pointer, taking ownership of the object that is being pointed at. You are printing the value of the two pointers, and they have the same value because they are pointing at the same object in memory.
Is this a design flaw of
unique_ptr
?
No.
How do I overcome this issue?
What issue? There is no issue here. The unique_ptr
is acting as designed.
What is the use of
std::make_unique()
?
To more efficiently allocate memory, construct an object in that memory, and take ownership of that memory with a new unique_ptr
, all in one operation.
I tried changing the line
std::unique_ptr<foo> uptr(ptr);
tostd::unique_ptr<foo> uptr(std::make_unique<foo>(*ptr));
but nothing changed.
It should have.
The expression std::make_unique<foo>(*ptr)
is creating a new foo
object that is separate from the object that ptr
is pointing at. It will pass the dereferenced *ptr
object to the new foo
object's copy constructor.
And then, in the expression std::unique_ptr<foo> uptr(...);
, uptr
is moving the unique_ptr
that make_unique()
returns, which is pointing at the new foo
object. So uptr
now owns that foo
object.
Subsequently, your 2 prints should be outputting different values, since ptr
and uptr
are pointing at different foo
objects.
Note that in this situation, you would be leaking the foo
object that ptr
is pointing at, since no unique_ptr
is taking ownership of that object. So you are responsible for delete
'ing it manually.
This code:
foo *ptr = new foo(5);
std::unique_ptr<foo> uptr(ptr);
Can be changed to this instead:
std::unique_ptr<foo> uptr = std::make_unique<foo>(5);
or:
auto uptr = std::make_unique<foo>(5);
std::make_unique()
allocates and constructs the type specified in its template argument, passing the input parameters to that object's constructor.