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

Collection of elements that takes unique_ptr, but stores them as shared_ptr to provide access as weak_ptr


Is the following considered a bad/good practice? Why?

class Scene
{
    public:
        std::weak_ptr<Node>                 AddChild(std::unique_ptr<Node> node);
        std::unique_ptr<Node>               DetachChild(std::string name);

    private:
        std::vector<std::shared_ptr<Node>>  mNodes;
};

The idea is to make sure that Scene is the only owner of its children, and force to transfer ownership when adding a Node to that scene. However there should be a way for clients to have access to Nodes stored inside the Scene (without constantly looking up it by name/id).

Typical example would be something like:

  1. You have an UI window that you create (Scene)
  2. You add several text labels to it (Nodes)
  3. You want to save pointers to those labels so that later you could modify the text.

Alternative would be also to add using unique_ptr but return raw pointer, however the text could be updated in different classes and there should always be a check that the label exists (e.g. there can be a running Tween function that updates text in a loop which you trigger in "Fire and forget" mode and just want to make sure it won't access dangling pointer(think of cocos2d actions) Even if checking just means just catching and assert).


Solution

  • What you want is not possible due to the nature of shared_ptr. While unique_ptr allows you to extract the pointer without deleting it, shared_ptr does not.

    What you want is also not good practice, due to threading. Even if you implement your own smart pointer that does exactly what you want, how do you use it? It would be possible for a user to lock one of those handles, then in another thread you convert your internal smart pointer to a unique_ptr. How does that work? What you have is two pieces of code both conceptually owning the object, but only one of them controls its lifetime.

    Unless you forbid threading, that is very much in Murphy's realm.

    Things like this are precisely why shared_ptr cannot abandon its ownership of the pointer.

    Basically I need unique_ptr that can return a pointer that will become null as soon as the original unique_ptr gets destroyed.

    This is precisely why weak_ptr does not provide direct access to the internal pointer. You have to lock it, which guarantees that you can access the memory for as long as you keep that shared_ptr around.

    This coding pattern prevents the exact kind of data races that your idea will create.

    shared_ptr was designed by some very smart people working over a long period of time. Ignore its wisdom at your own peril.