使用shared_ptr获得唯一所有权(种类)-这是一种好习惯吗? [英] Using shared_ptr for unique ownership (kind of) - is this good practice?

查看:47
本文介绍了使用shared_ptr获得唯一所有权(种类)-这是一种好习惯吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 renderComponentCreated event which the RenderSystem subscribes to. Using an event args object, I pass a RenderNode as data which contains the information the renderSystem needs to draw the thing (drawable, position and type).

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

So far so good. Now, when the renderComponent is deleted, I want the RenderNode to be removed from the RenderSystem automatically while still leaving the option to remove it manually e.g. as a reaction to some event. This could be done using a RenderComponentRemoveNodeEvent which again the RenderSystem subscribes to.

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

Now the 'problem'. From my understanding (and from what I want) the renderNode should be something uniquely owned by the RenderComponent (hence unique_ptr). However, this would require me to either copy (and implement a comparison operator for the renderNode --> to be able to find it when I want to remove it) or pass a reference / raw pointer to the renderNode. However, (If I'm correct) there is no way to know whether a reference still refers to a valid object which would mean that automatic removal could not be implemented.

我的解决方案是使RenderNode(由RenderComponent唯一拥有)共享并传递指向事件的弱指针.RenderSystem现在还维护一个弱指针列表,它检查弱指针是否仍指向有效对象,如果没有,则自动删除它们.因此,本质上我想要从唯一的指针创建弱指针.但是,现在的情况是,有人可以从弱指针创建共享指针,并使RenderNode的生存期比应有的更长.由于托管对象本身(RenderNode)包含对其他对象的引用,这些对象的存在时间不超过RenderComponent,这可能会导致严重的问题.

My solution was to make the RenderNode (that is uniquely owned by the RenderComponent) shared and pass weak pointers to the event. The RenderSystem also now maintains a list of weak pointers that it checks for whether they are still pointing to a valid object and automatically removes them if not. So essentially what I would have wanted is creating a weak pointer from a unique one. However, the way it is now, someone could just create a shared pointer from the weak pointer and keep the RenderNode alive longer than it should. Since the managed object itself (RenderNode) contains references to other objects that will not exist longer than the RenderComponent this may cause serious issues.

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

My question now: can this be considered good design or have I missed something?

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

PS: Sorry if this explanation reads a bit clunky (English is not my native) and thanks for your help!

推荐答案

使用 std :: weak_ptr 授予对可能被破坏的对象的访问权限肯定没有错,这就是它的发明为了.但这确实需要对象本身由 std :: shared_ptr 持有.这不仅掩盖了让对象的生命周期受其父对象控制的意图,而且还强制了对象的动态分配,并使其无法成为父对象的成员变量.

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.

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

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

这篇关于使用shared_ptr获得唯一所有权(种类)-这是一种好习惯吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆