sydridgm
sydridgm

Reputation: 1062

Why need to set rvalue reference to null in move constructor?

I've read the post: Why do we need to set rvalue reference to null in move constructor?

It said that during a move, you just copied the pointer from one object to another, so two pointers points to the temporary object. And when the temporary object goes out of scope, its destructor will run. And then the object pointed by 2 pointers would be deallocates. So the pointer field of object constructed by move constructor would point to unavailable place.

However, look at following code:

int *p = new int(3);
int *q = p;
delete p;
std::cout << *q << std::endl;

after delete the pointer p, pointer q could still access the int object.

So, is that just because when the object is accessed by rvalue reference causes the difference?

Upvotes: 0

Views: 642

Answers (5)

LogicStuff
LogicStuff

Reputation: 19607

Both the question you linked and your question is trying to avoid undefined behavior upon dereferencing of q.

The thing is that, (in the linked question) you have to know that you can't dereference q anymore (you aren't managing it) by setting it to nullptr. But here, it's your business to not to dereference q after the delete. It's extremely predictable that q will be deleted before std::cout << *q << std::endl;. It's your fault that you're dereferencing it!

Nevertheless, setting q to nullptr helps with debugging, delete q is no longer UB, and nullptr signifies a pointer to non-existent object.

Upvotes: 0

Loki Astari
Loki Astari

Reputation: 264491

This is the problem with pointers. They do not convey ownership semantics.

You should be using something like std::unique_ptr. When you move the pointer from one object to another the old object recognizes it no longer owns the object and thus you can;t access the data.

Simple solution. Like the rest of the C++ community stop using pointers like this.

std::unqieu_ptr<int> p(new int(3));
std::unique_ptr<int> q = p;              // Fails to compile


std::unqieu_ptr<int> p(new int(3));
std::unique_ptr<int> q = std::move(p);   // explicitly transfer ownership.


// delete p;  // No longer need this as when the onbject goes out of scope
              // it is deleted.

Upvotes: 0

Luchian Grigore
Luchian Grigore

Reputation: 258618

No you can't. It appears to work, but it's entirely undefined behavior. The memory is released to the runtime.

When you move something, you're effectively saying the original object is no longer valid - you only access its original contents from the moved-to object. Furthermore, the set to NULL after a move prevents a double-delete - when the original objects goes out of scope and attempts to free that memory, it will either fail straight away (if the new object already free'd it) or the new object will fail when it itself attempts to release that memory.

Upvotes: 2

cadaniluk
cadaniluk

Reputation: 15229

Accessing deleted memory of the free store is undefined behavior.

On move construction, the pointer of the object constructed is set to the pointer of the rvalue, which in turn is set to nullptr afterwards.
The object referenced by the rvalue ends its lifetime thereafter, after all it is a temporary. Therefore, the pointer set to nullptr is not expected to be used anymore. When it is destructed, delete nullptr; is executed, i.e., a no-op.
OTOH, when the lifetime of the constructed object ends, the actual resource is deleted - the pointer to it was assigned in the move constructor.

Upvotes: 1

Edward Strange
Edward Strange

Reputation: 40867

No, after delete the pointer p, pointer q cannot still access the int object. If you think otherwise because you ran it and got 3 in the output--that's because you caused undefined behavior and UB can "work" as one of its many, many options.

In general what happens is that the memory address hasn't been reused yet, still belongs to the program (so it has permission to access and the value has not been erased), so you'll see whatever value was deleted in that memory. All this means that it will appear as if the code is perfectly OK when it's not. You could also print using *p in this case and it would be no different. The p and q variables both contain the same address...which was freed by a delete.

One interesting aspect of this is that high security applications, or encryption applications like GPG, manually fill deleted memory with garbage so that it can't just be sniffed out--giving access to private keys out to the world. Otherwise the key would still just be written in RAM until someone writes something else there!

Upvotes: 0

Related Questions