所有权和如何避免shared_ptr

Ownership and how to avoid shared_ptr

本文关键字:ptr shared 何避免 所有权      更新时间:2023-10-16

我正在尝试为游戏引擎编写一个简单的事件管理器类和侦听器。在通常的实现(例如McShaffry)中,事件管理器注册侦听器,原则上将shared_ptr作为私有成员保存到侦听器。

我在很多情况下看到人们说shared_ptr和like应该避免(例如这里)。因此,我试图找到实现事件管理器而不共享侦听器所有权的方法。

我想到的一种方法是为侦听器分配唯一的id,并向事件管理器注册它们的id。然后,侦听器负责在事件管理器更新后"询问"事件管理器,是否在其id下有任何事件可用。

我想问一下,在这种情况下,是否有更清晰和/或标准的方法来避免共享所有权,但也一般。例如,我对听众也有同样的问题。侦听器需要存储指向其父类(或侦听对象)的指针,以便在处理事件时调用其父类的方法。

正如Mat的注释所说,一般没有理由不使用智能指针。也就是说,警示性警告确实似乎适用于您的情况:据我所知,您没有共享所有权;事件管理器拥有侦听器的唯一所有权。因此,shared_ptr在这里是不合适的。

另一种选择是使用unique_ptr,它在许多方面是shared_ptr硬币的另一面。但是,根据您对侦听器建模的方式,甚至可以通过简单地将具体的实例保存到事件管理器中来避免这种情况。如果没有更详细的描述,你根本不可能说你是否需要指针,但是如果你不需要它们,那么,是的,建议适用:当具体对象需要时,不要使用(智能)指针。

最后,如果侦听器是其所有权在其他地方管理的对象,请考虑简单地使用原始指针指向这些对象:在这种情况下,事件管理器根本不是对象的所有者——既不是唯一的所有者,也不是共享的所有者。虽然这是我的首选方式,但它需要仔细分析侦听器的生命周期,以确保事件管理器不会指向不再存在的侦听器。

shared_ptr有过度使用的倾向;例如,它经常被推荐用于解决模糊说明的指针问题。它不是好的设计的替代品,除非有一个基于理解正在编写的代码中的对象生命周期问题的设计,否则不应该使用它。

从个人经验来看,shared_ptr是一个很好的工具,但有时可能不是适合这项工作的工具。如果代码完全在您的控制之下,99.9%的情况下,shared_ptr可能会使您的工作更轻松。你需要确保你没有这样的想法:

Foo *f = new Foo();
shared_ptr<Foo> fptr(f);
shared_ptr<Foo> fptr2(f);

这将导致f的内存被fptr1fptr2释放。相反,您应该这样做:

Foo *f = new Foo();
shared_ptr<Foo> fptr(f);
shared_ptr<Foo> fptr2 = fptr;

在第二种情况下,将一个共享指针赋值给另一个共享指针将使引用计数增加。

另一个你可以在shared_ptr遇到麻烦的地方是,如果你需要传递一个裸指针给一个函数(这可能发生,如果你需要传递this作为方法的第一个参数,或者你依赖于第三方库)。你可以从shared_ptr中获得裸指针,但你不能保证它指向的内存地址仍然存在,因为引用计数器不会增加。

您可以通过保留额外的shared_ptr来解决这个问题,尽管这可能很麻烦。

还有其他形式的智能指针。例如,OpenSceneGraph有一个ref_ptr,它比shared_ptr更容易工作。需要注意的是,它指向的所有对象都必须来自Referenced。然而,如果你能接受这一点,我认为让真正糟糕的事情发生要困难得多。

在某些情况下,shared_ptr是过度的,或者没有正确地显示所需的语义(例如传递所有权)。

你需要做的是看看你的设计,看看你需要什么样的所有权模型。如果你需要/想要共享所有权,那么只需使用shared_ptr来建模。如果共享/ref计数的所有权不合适,请使用其他智能指针。

您的情况是否适合这里描述的auto_ptr的良好使用:http://www.gotw.ca/publications/using_auto_ptr_effectively.htm(本周的导师«有效使用auto_ptr)

据我所知,您构建一个侦听器,然后将其交给事件管理器。因此,事件管理器可以被视为一个"接收器"。

使用auto_ptr技术,您的事件管理器可以干净安全地获得您提供给他的侦听器的完全所有权。