Mendes
Mendes

Reputation: 18561

C++ Move a unique_ptr to a vector and continue using it

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.

Upvotes: 0

Views: 6130

Answers (2)

AndyG
AndyG

Reputation: 41220

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.

Upvotes: 0

Russell Greene
Russell Greene

Reputation: 2281

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.

Upvotes: 3

Related Questions