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.
>Solution :
Is the raw pointer being passed into the copy constructor?
Not the copy constructor, no (that takes another unique_ptr as input). It 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 std::unique_ptr<foo> uptr(...);, uptr is taking ownership of the returned unique_ptr that is pointing at the new foo object.
Subsequently, your 2 prints should be outputting different values, since ptr and uptrare pointing at differentfoo` objects.
Note that in this case, you would be leaking the foo object that ptr is pointing at, since no std::unique_ptr is taking ownership of that object.
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.