Consider the following code:
#include <memory>
struct Foo { std::unique_ptr<Foo> next; };
void f(Foo &foo) { foo = std::move(*foo.next); }
int main() {
Foo foo{};
foo.next = std::make_unique<Foo>();
foo.next->next = std::make_unique<Foo>();
f(foo);
}
By doing foo = std::move(*foo.next);
, foo.next.next
is moved to foo.next
.
If foo.next
is invalidated as a first step, the object to which it points could be deleted immediately. This would lead to the deletion of foo.next.next
, that is the object that I'm trying to move to foo.next
.
I'm pretty sure I'm missing something in my reasoning, but I can't figure out what's wrong.
Is it a safe operation? Where does the standard reassure me about that?
Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.
The unique_ptr shall not be empty (i.e., its stored pointer shall not be a null pointer) in order to be dereferenciable. This can easily be checked by casting the unique_ptr object to bool (see unique_ptr::operator bool). It is equivalent to: *get().
std::unique_ptr s are useful when you want to tie the lifetime of the object to a particular block of code, or if you embedded it as member data inside another object, the lifetime of that other object. The object exists until the containing block of code is exited, or until the containing object is itself destroyed.
In modern C++ programming, the Standard Library includes smart pointers, which are used to help ensure that programs are free of memory and resource leaks and are exception-safe.
I think it's all perfectly safe. When you call the f()
function on foo
, the move assignment operator of class Foo
will invoke std::unique_ptr<Foo>::operator=(std::unique_ptr<Foo>&&)
. Now, the C++14 standard, §20.8.1.2.3, comma 2, says:
Effects: Transfers ownership from
u
to*this
as if by callingreset(u.release())
followed byget_deleter() = std::forward<D>(u.get_deleter())
.
At §20.8.1.2.5, comma 4, we find the behavior of reset()
:
Effects: assigns
p
to the stored pointer, and then if the old value of the stored pointer,old_p
, was not equal tonullptr
, callsget_deleter()(old_p)
. [ Note: The order of these operations is significant because the call toget_deleter()
may destroy*this
. —end note ]
So, we can argue that the stored pointer will be replaced and then the old stored pointer will be deleted, in this order. Thus, everything is fine and well defined.
Furthermore, when you will enter into the reset()
function, the *foo.next
object will already have been release()
d, so the pointed object wouldn't be destroyed with it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With