c++ - 使用shared_ptr来获得唯一的所有权(某种程度)——这是一个好的做法吗?

标签 c++ shared-ptr smart-pointers software-design

这很难解释,但我会尽力。所以,我有一个 RenderComponent、EventManager 和 RenderSystem。在我的 RenderComponents 构造函数中,我引发了 RenderSystem 订阅的 renderComponentCreated 事件。使用事件参数对象,我将 RenderNode 作为数据传递,其中包含 renderSystem 绘制事物所需的信息(可绘制、位置和类型)。

到目前为止一切顺利。现在,当删除 renderComponent 时,我希望自动从 RenderSystem 中删除 RenderNode,同时仍然保留手动删除它的选项,例如作为对某些事件的 react 。这可以使用 RenderComponentRemoveNodeEvent 来完成,RenderSystem 再次订阅该事件。

现在是“问题”。根据我的理解(以及我想要的),renderNode 应该是 RenderComponent 唯一拥有的东西(因此是 unique_ptr)。但是,这需要我复制(并为 renderNode 实现一个比较运算符 --> 以便在我想要删除它时能够找到它)或将引用/原始指针传递给 renderNode。但是,(如果我是正确的)无法知道引用是否仍然引用有效对象,这意味着无法实现自动删除。

我的解决方案是共享 RenderNode(由 RenderComponent 唯一拥有)并将弱指针传递给事件。渲染系统现在还维护一个弱指针列表,它检查它们是否仍然指向有效对象,如果不是,则自动删除它们。所以本质上我想要的是从一个独特的指针创建一个弱指针。然而,按照现在的方式,有人可以从弱指针创建一个共享指针,并使 RenderNode 的存活时间比应有的时间长。由于托管对象本身 (RenderNode) 包含对其他对象的引用,而这些对象的存在时间不会比 RenderComponent 长,这可能会导致严重的问题。

我现在的问题:这可以被认为是好的设计还是我错过了什么?

PS:抱歉,如果这个解释读起来有点笨拙(英语不是我的母语),感谢您的帮助!

最佳答案

使用 std::weak_ptr 授予对可能被销毁的对象的访问权限当然没有任何问题,这就是它的发明目的。但它确实需要对象本身由 std::shared_ptr 保存。这不仅掩盖了您让对象生命周期由其父对象控制的意图,还强制动态分配对象并阻止它成为父对象的成员变量。

另一种方法是通过句柄跟踪指针,并使用句柄管理器来跟踪对象是存活还是死亡。实现此目的的最安全方法是使管理器成为您正在跟踪的对象的基类,这样 RAII 可确保它始终是最新的。这是该概念的示例实现。注意:未经测试。

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;
};

以下是如何使用它的示例:

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;
            }
        }
    }
};

关于c++ - 使用shared_ptr来获得唯一的所有权(某种程度)——这是一个好的做法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47317531/

相关文章:

c++ - 使用棋盘标定的相机标定

c++ - std::shared_ptr 或 std::unique_ptr 赋值运算符重载

c++ - 关于在 winsock 的 addrinfo 结构中使用智能指针的问题

c++ - 是否可以使shared_ptr协变?

c++ - 试图绘制点云,只是 opengl 中心的一个点

c++ - 在调试中构造 C++ 类的详细信息

C++,运行时错误 : member call on null pointer of type

c++ - 从体积或区域中的起点向外迭代而不对其进行排序

c++ - shared_from_this() 如何在继承自基类的派生类中工作,该基类继承自 enabled_shared_from_this