Using shared_ptr for unique ownership (kind of) - is this good practice?

后端 未结 1 1081
南笙
南笙 2020-12-20 00:32

this is quite hard to explain but I\'ll try my best. So, I have a RenderComponent, EventManager and RenderSystem. In my RenderComponents constructor, I raise a renderCompone

相关标签:
1条回答
  • 2020-12-20 01:03

    There's certainly nothing wrong with using std::weak_ptr to grant access to an object that might be destroyed, that's what it was invented for. But it does necessitate that the object itself be held by a std::shared_ptr. Not only does this mask your intent to have the object lifetime controlled by its parent, it forces dynamic allocation of the object and precludes it from being a member variable of the parent.

    An alternate approach is to keep track of the pointer through a handle, and have a handle manager that tracks whether the object is alive or dead. The safest way to implement this is to make the manager a base class of the object you're tracking, that way RAII ensures it is always up to date. Here's a sample implementation of that concept. Note: untested.

    template<class Derived>
    class HandleBased
    {
    public:
        typedef uint64_t handle_t;
    
        HandleBased() : m_Handle(NextHandle())
        {
            Map()[m_Handle] = this;
        }
    
        ~HandleBased()
        {
            auto it = Map().find(m_Handle);
            Map().erase(it);
        }
    
        handle_t ThisHandle()
        {
            return m_Handle;
        }
    
        static Derived* FindPtr(handle_t h)
        {
            auto it = Map().find(h);
            if (it == Map().end())
                return null_ptr;
            return static_cast<Derived*>(it->second);
        }
    
    private:
        static handle_t NextHandle()
        {
            static handle_t next = 0;
            return next++;
        }
    
        static std::unordered_map<handle_t, HandleBased*>& Map()
        {
            static std::unordered_map<handle_t, HandleBased*> the_map;
            return the_map;
        }
    
        handle_t m_Handle;
    };
    

    And here's an example of how you'd use it:

    class RenderNode : public HandleBased<RenderNode>
    {
    };
    
    class RenderComponent
    {
        std::unique_ptr<RenderNode> node1;
        RenderNode node2;
    
    public:
        void Setup(RenderSystem& rs)
        {
            node1 = new RenderNode;
            rs.nodes.push_back(node1->ThisHandle());
            rs.nodes.push_back(node2.ThisHandle());
        }
    };
    
    class RenderSystem
    {
    public:
        std::list<RenderNode::Handle> nodes;
    
        void DoRender()
        {
            for (auto it = nodes.begin(); it != nodes.end(); )
            {
                RenderNode* p = RenderNode::FindPtr(*it);
                if (p == NULL)
                    it = nodes.erase(it);
                else
                {
                    p->DoSomething();
                    ++it;
                }
            }
        }
    };
    
    0 讨论(0)
提交回复
热议问题