我们应该在取消注册时删除观察者吗?

时间:2011-05-31 11:16:33

标签: c++ observer-pattern

Observer *o = New Observer();
Subject *s = new Subject() ;
s->register(o);

//Is it a good practice to delete the observer in the unregister function?
//I feel it is not. As the Observer object might still be in use,  for example , 
//it might be registered to another Subject. 
s->unregister(o);  

//So it is safe to rely on the client code to delete the object or rely on the smart pointer things
delete o;

我想确认关于谁应该删除观察者对象,我的上述理解是否正确。

8 个答案:

答案 0 :(得分:5)

我同意你的观察。在取消注册函数中删除观察者并不是一个好习惯 - 因为“创建资源的人必须负责删除资源”

这将避免

  • 创作者所感知的魔法行为。
  • 代码行为将被很好地定义 - 创建者必须删除。这将为理解新系统开发人员的开发奠定整体基础。

在所有具有不同术语的书籍中都会讨论相似的主题。

答案 1 :(得分:4)

我会说使用智能指针,因为不需要记得明确地调用delete

答案 2 :(得分:2)

从设计的角度来看,如果观察者得到很好的实施,取消注册主题应该是多余的(虽然没有错)。如果可以强制执行的行为依赖于外部代码,则可以始终将其视为设计不佳。

关于智能指针的使用,如果由于某种原因需要对销毁点进行控制(在给定时间点关闭文件以避免以后代码中的访问问题)那么你应该明确删除,否则它是依靠智能指针更加舒适。这就是存在的原因。

答案 3 :(得分:2)

由于Subject未分配Observer,因此不应尝试取消分配Subject。这允许Observer的客户端以其选择的任何方式管理new的生存期和分配策略(自定义分配器,静态分配,自动变量)。它不会强制客户端使用Observer

显然,客户有责任不让Observer o; Subject s; s.register(&o); // could take a reference // ... s.unregister(&o); // No potential for forgotten deletes 在“未注册”之前被销毁。

E.g。

{{1}}

答案 4 :(得分:1)

这取决于您的设计。我个人更喜欢删除Observer会自动将其与主题解除关联,即在析构函数中注销自己。这样就省去了取消注册的麻烦,这需要你在破坏点引用Subject和Observer。

答案 5 :(得分:0)

Smartpointer或客户端代码(如果可能,不转让所有权) 如果客户端代码创建了对象,则使用相同的代码销毁它:)

答案 6 :(得分:0)

反对删除的另一个论点是Subject对象通信

通过删除对象,您的Subject对象拥有该对象的所有权,因为它控制了它的生命周期。由于原始指针被传递到对象中,而不是auto_ptr或unique_ptr,我认为作为Subject的用户,它不会在不查看代码的情况下获取对象的所有权。所以我会说Subject的接口不会传达它取得对象的所有权。所以主题不应该删除它,因为其他人(可能)拥有它的所有权。

答案 7 :(得分:0)

一般情况下,除非容器也负责创建指针,否则应避免像删除指向容器中对象的指针等副作用。

使用Observer模式的指针问题也比这更深入。

观察者固有地对主体有某种参考。如果该引用是直接引用(例如,不是句柄),那么如果该对象超出范围或被删除,则当Observer进行Notify(...)调用时,它将引发异常(或更糟)。因此,您必须确保在删除主题时从观察者中分离(...)。

忘记这样做是一个问题,所以我喜欢将它构建到虚拟析构函数的基类中的想法。这也意味着Subject知道Observer(对它的引用),因此它可以调用Detach(...)。这使你陷入与观察者类似的情况(即如果它被删除)。我发现最简单的方法是让Observer实际上是一个单例,Subjects注册特定的通知,而不仅仅是“我有一些状态让你更新”。这与GoF第298页第7项“明确指定利益修改”一致。而且,简单地说,有点缩小更新字段是有意义的。它还允许在任何地方更轻松地连接任何主题表单。

需要考虑的另一个案例是在Notify(..)迭代期间删除Subject时。在你的观察者中,你有一个主题,A,B和C的列表。你改变状态并在A上调用通知(...)。删除B.现在你继续调用B上的通知(...) ,这是不存在的。如果你在析构函数中有Detach(...),B可能不再在列表中。但是如果你选择迭代你开始使用的主题的副本而不是实际的列表(在那里,那样做),它可能仍然存在。因此,Observer需要在迭代期间为Notify(...)处理Detach(...)调用。

我在博客here上发布了一个详尽的例子。