Search code examples
c++c++11shared-ptrunique-ptrweak-ptr

Is there such thing as a weak_ptr that can't be locked (promoted to shared_ptr)? If not, why?


Maybe this question has been asked before, but I've never found a satisfactory answer. Also, for the purposes of simplicity assume I'm talking about a single-threaded application.

So, what I've heard a number of times is that if you have an object that is non-owned and whose lifetime is guaranteed, you should reference it with a raw pointer. The object's owner would use a unique_ptr, and hand out raw pointers as necessary.

But what if the object is non-owned, and the lifetime is not guaranteed? Then you can use a weak_ptr, yes. But then anyone who is handed a weak_ptr could be naughty and keep it locked, such that the object's owner can't cause the object to be destroyed. Sometimes this may not be a problem, but sometimes it is. For example, when the owned object represents some system resource which must be relinquished at a certain time.

You may say "well, then you should just make sure no one keeps the weak_ptr locked!" But that is just not ideal (in my opinion) from an OO design standpoint, as it creates a dependency between the "owner" object and any object that gets a weak_ptr from it. You might as well make the argument "you don't need to return const references; you should just make sure no one modifies the reference."

With Qt, you have the QPointer, which is basically what I'm looking for. It checks that the object hasn't been destroyed, but it can't prevent the object from being destroyed. I realize this isn't thread-safe, but again, I'm talking about the context of a single thread.

So why isn't there something similar for C++11? I'm sure I could make a wrapper around weak_ptr that accomplishes what I'm after. But I wonder if I'm going about this all wrong.


Solution

  • You can do this with pure Standard C++ using shared_ptr<unique_ptr<T>>.

    Observers received only a shared_ptr<const unique_ptr<T>>, allowing them to look but not touch. The owner, having a non-const smart pointer, can at any time call reset() on the unique_ptr to destroy the instance. At that time all the observers can also see that the unique_ptr has become empty.

    Obvious threading and re-entrance caveats apply (you need to check the unique_ptr for having a valid pointer again after each callback you invoke, etc).

    And if there should be multiple owners, it's a bit more work. You will need one shared_ptr<T*>, giving observers a shared_ptr<T* const>. And a separate shared_ptr<T> to manage the object lifetime. The shared_ptr<T*> will need to be manually filled with nullptr (The T*, not the shared_ptr) in the object's destructor.