I'm looking at the implementation of a singly linked list using unique_ptr
on https://solarianprogrammer.com/2019/02/22/cpp-17-implementing-singly-linked-list-smart-pointers/. My question pertains to the following method:
3 struct List {
4 List() : head{nullptr} {};
5
6 // ...
7
8 void pop() {
9 if(head == nullptr) {
10 return;
11 }
12
13 std::unique_ptr<Node> temp = std::move(head);
14 head = std::move(temp->next);
15 }
16
17 // ...
18 };
I am wondering why the temporary is needed here? Why couldn't you simply do head = std::move(head->next)
? Is this because it will result in a memory leak? When head
is reassigned, does unique_ptr
automatically free the current memory it's pointing to?
I was under the impression that smart pointers are fool proof from memory leaks. It seems in this case there might be a memory leak for the original head
because there would no longer be a smart pointer pointing to it?
I am wondering why the temporary is needed here?
It is not really needed, but it is not bad to use, either.
Why couldn't you simply do
head = std::move(head->next)
? Is this because it will result in a memory leak?
You can. There will be no leak in this example.
When
head
is reassigned, doesunique_ptr
automatically free the current memory it's pointing to?
Yes. However, the old pointer will not be delete
'd until after ownership of the new pointer is transferred first. Per cppreference:
https://en.cppreference.com/w/cpp/memory/unique_ptr/operator%3D
Transfers ownership from
r
to*this
as if by callingreset(r.release())
followed by an assignment ofget_deleter()
fromstd::forward<E>(r.get_deleter())
.
https://en.cppreference.com/w/cpp/memory/unique_ptr/reset
Replaces the managed object.
Given
current_ptr
, the pointer that was managed by*this
, performs the following actions, in this order:
- Saves a copy of the current pointer
old_ptr = current_ptr
- Overwrites the current pointer with the argument
current_ptr = ptr
- If the old pointer was non-empty, deletes the previously managed object
if(old_ptr) get_deleter()(old_ptr)
.
So:
In the case where temp
is used, the pointer to the old node in head
will first be moved to temp
via its move constructor, resetting head
to hold a nullptr
. Then head.operator=
will call next.release()
and acquire that pointer. And then temp
will go out of scope, delete
'ing the old node.
In the case where temp
is not used, head.operator=
will call next.release()
, save its old pointer and replace it with the released pointer, and then delete
the saved pointer.
No leak either way.
I was under the impression that smart pointers are fool proof from memory leaks.
If used properly, yes.
It seems in this case there might be a memory leak for the original
head
because there would no longer be a smart pointer pointing to it?
There is no leak, as there is always a unique_ptr
referring to the old node, until pop()
exits and temp
is destroyed, delete
'ing the old node with it. Even if temp
is omitted, the old node is still destroyed properly after ownership of its next
pointer is transferred.