Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Referring to a "std::unique_ptr" that you don't own (use a raw pointer?)

Typically if you are using a std::shared_ptr to point to an object and you want to create another pointer to that object that does not share ownership you would create a std::weak_ptr.

// Create a shared pointer to own the object
std::shared_ptr<int> p = std::make_shared<int>(42);

// Create a weak pointer (that does not own the object)
std::weak_ptr<int> q(p);

// Use the weak pointer some time later
if (std::shared_ptr ptr = q.lock()) {
  // use *ptr
}

My question is, how do you do this when it comes to std::unique_ptr?

Using a unique pointer ensures that the current resource is owned exclusively by the std::unique_ptr itself. But what if I want to create a pointer to the same resource that does not own that resource? I can't use a std::weak_ptr because weak pointers are designed to work with the reference count from a std::shared_ptr. Would I just use a raw pointer here? Or is there a better alternative?

// Create a unique pointer to own the object
std::unique_ptr<int> p = std::make_unique<int>(42);

// Create a non-owning pointer to the same object
// Is this really the best way?
int* q = p.get();

// Use the pointer some time later
if (q != nullptr) {

  // Imagine this may be multithreaded...
  // what happens if p.reset() is called by another thread while the current thread is RIGHT HERE.

  // use *q
}

The only way I can think of creating a non-owning pointer to an object owned by a std::unique_ptr would be to use a raw pointer, but as you can see from the code above this may cause issues in threaded applications. Is there a better way to achieve the same goal?

like image 362
tjwrona1992 Avatar asked Mar 06 '19 17:03

tjwrona1992


Video Answer


2 Answers

Based on your last example, this is a scenario where std::shared_ptr and std::weak_ptr should be used.

std::unique_ptr and a non-owning Raw Pointer should be used in the scenario where you have a guarantee that the smart pointer will outlive the raw pointer.

class A {
    std::unique_ptr<int> ptr = std::make_unique<int>(5);
public:
    int* get_ptr() const{return ptr.get();}
};

class B {
    A a;
public:
    void do_something() {
        //int * ptr = a.get_ptr();//Valid, but not advised
        int & ref = *a.get_ptr();//Preferred
        ref++;
    }
};

If you can make this guarantee, you should be using std::unique_ptr and a raw pointer to represent this object. This is ideomatically correct.

If, however, you can't guarantee lifetime at the time you need to manipulate the object, then references should be provided by std::weak_ptr, which are used to acquire ownership (even if only temporarily!) to make changes.

class A {
    std::shared_ptr<int> ptr = std::make_shared<int>(5);
public:
    std::weak_ptr<int> get_ptr() const {
        return ptr;//Implicitly converts
    }
    void destroy() {
        ptr.reset();
    }
};

class B {
    std::weak_ptr<int> ptr;
public:
    B(std::weak_ptr<int> ptr) : ptr(ptr) {}
    void do_something() {
        if(auto owned_ptr = ptr.lock()) {//owned_ptr will be deduced to be of type std::shared_ptr<int>
            *owned_ptr++; //Guaranteed to only execute if the pointer is still valid
        }
    }
};

int main() {
    A a;
    B b(a.get_ptr());
    if(get_random_value() > 10)
        a.destroy();
    b.do_something();
}
like image 195
Xirema Avatar answered Oct 24 '22 23:10

Xirema


It depends on whether the extra pointer may outlive the unique_ptr.

  • If the non-owning pointer absolutely cannot outlive the unique_ptr: Make it a plain pointer. Whoever gets this kind of pointer can assume it's valid, but cannot presume to own the memory it points to.
  • If the non-owning pointer might outlive the unique_ptr: Your original pointer is not really "unique"; and you should replace the unique_ptr with a shared_ptr and pass a weak_ptr as the non-owning copy.

I just noticed that this answer is basically a brief summary of Xirema's answer. Please upvote/accept that answer!

like image 38
einpoklum Avatar answered Oct 24 '22 23:10

einpoklum