0

I´m starting playing around with std::unique_ptr and I just don´t want to mess up things.

On my code, I´m creating a std::unique_ptr, storing it on a vector for later use in another context and continue using the pointer:

#include <iostream> #include <string> #include <memory> #include <vector> class MyClass { public: void doWhatever () { std::cout << "Var = " << variable << std::endl; } int variable = 0; }; class Controller { public: std::vector<std::unique_ptr<MyClass>> instances; }; class OtherClass { public: void myFunction() { Controller control; std::unique_ptr<MyClass> p(new MyClass); control.instances.push_back(std::move(p)); // Continue using p. p->doWhatever(); // Is this valid ? p->variable = 10; // Is this valid ? p->doWhatever(); // Is this valid ? } }; int main() { OtherClass cl; cl.myFunction(); } 

The code compiles, but I´m getting segmentation fault on execution.

I imagine that calls to p after moving the pointer to the vector are invalid.... If so, what would be the solution here ? Moving to a shared_ptr ?

OBS: I cannot move to vector after using the pointer. In real application this will be running on a multi-threaded environment where one thead is using the vector data and the other continue using the original pointer p.

Thanks for helping.

2
  • 5
    After doing std::move(p) you're not allowed to use p anymore! Commented Jul 14, 2015 at 18:49
  • 2
    @zenith p itself can be (re)used, *p can't Commented Jul 14, 2015 at 18:56

2 Answers 2

3

The name of unique pointer says it all. It is meant to be completely unique so when it goes out of scope it can be safely deleted.

When you use std::move on the pointer, it casts it to a rvalue reference hat then invokes the move constructor, meaning it is moved, not copied. It is just like if you move a plate from the dishwasher to the cabinet, it isn't in the dishwasher. In code, that means that the unique_ptr that is moved from is set to nullptr

Alternatives:

  1. Use a ref counted shared_ptr. This will allow you to have multiple instances and after the last one is deleted the destructor will be called. Another plus of using this is that the vector could store weak_ptrs if it shouldn't keep the object from being destroyed.
  2. Cache the raw pointer before using std::move on the unique_ptr to move it into the vector. This potentially unsafe because if you try to use that pointer after the vector is destroyed it is undefined behaviors because it would be a dangling reference.
Sign up to request clarification or add additional context in comments.

7 Comments

Change the type to shared_ptr would be enough here or some more task is needed ?
If you use a shared_ptr in both the vector and the original ptr it should work fine. Also, when constructing the shared_ptr consider using make_shared because it is one less allocation (this is also useful for unique_ptrs with make_unique) en.cppreference.com/w/cpp/memory/shared_ptr/make_shared
Worked like a charm... We need to remove the std::move also from the assignment. Definitive code: std::shared_ptr<MyClass> p = make_shared<MyClass>(new MyClass); control.instances.push_back(p)
Never lightly use a shared_ptr. They are expensive and only sensible if you're dealing with objects that lack a clear ownership. In the OP, there is no sense of ownership, so one cannot really make a recommendation.
@Walter they are fairly cheap if you aren't doing lots of copies and deletions. The atomic increment is the most expensive part other than the extra memory requirements.
|
0

In brief, no it is not valid to use any object after you've moved it. EDIT: Unless you are calling a function that has no preconditions (Thanks, Benjamin Lindley).

This is because when you std::move a unique_ptr, you are transferring ownership of that memory to another unique_ptr. (What's really happening is that std::move marks your unique_ptr as an r-value reference, and then the constructor for the other unique_ptr sees this and gleefully performs pointer stealing). The unique_ptr that you've moved into can now do whatever it wants, including delete the memory and invalidate any other reference you have.

You can dereference the pointer into a raw pointer (via unique_ptr::get()) to access what it's pointing at, and then move the unique pointer into the vector. However, the whole idea of a unique_ptr is sole ownership; your raw pointer could at any time become invalid.

2 Comments

You can use objects that have been moved from. As long as the operations you perform don't have any preconditions. And once you've verified preconditions, you're free to use the object as normal.
@BenjaminLindley: Thank you for that. A case I've not considered!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.