我在使用C++和STL实现观察者模式时遇到了一个有趣的问题。考虑这个经典的例子:
class Observer {
public:
virtual void notify() = 0;
};
class Subject {
public:
void addObserver( Observer* );
void remObserver( Observer* );
private:
void notifyAll();
};
void Subject::notifyAll() {
for (all registered observers) { observer->notify(); }
}
这个例子在每本设计模式的书中都能找到。不幸的是,现实中的系统更加复杂,所以第一个问题出现了: 一些观察者决定在被通知时向主题添加其他观察者。这使得“for”循环和我使用的所有迭代器无效。解决方法相当简单 - 我会对注册的观察者列表进行快照并遍历该快照。添加新的观察者不会使快照无效,所以一切看起来都没问题。但又出现了另一个问题:观察者决定在被通知时自行销毁。更糟糕的是,一个单一的观察者可以决定销毁所有其他观察者(它们是通过脚本控制的),这会使队列和快照无效。于是我发现自己正在迭代已被释放指针。
我的问题是,当观察者相互删除时,我应该如何处理?有没有现成的模式可以使用?我一直认为“观察者”是世界上最容易的设计模式,但现在似乎实现它并不那么容易...
感谢大家的关注,请允许我总结一下决策:
[1] “不要这样做” 很抱歉,但这是必须的。观察者是通过脚本控制并进行垃圾回收的。我无法控制垃圾回收以防止它们被释放;
[2]“使用boost::signal” 最有希望的决定,但我不能在项目中引入boost,这样的决定必须由项目负责人作出(我们正在Playstation下编写);
[3]“使用shared_ptr” 这将防止观察者被释放。一些子系统可能依赖于内存池清理,所以我认为我不能使用shared_ptr。
[4]“推迟观察者的销毁” 在通知时排队观察者进行删除,然后使用第二个循环来删除它们。不幸的是,我无法防止它们被释放,因此我使用一个技巧将观察者包装成某种“适配器”,实际上保留“适配器”的列表。在析构函数中,观察者将从其适配器中取消分配,然后我会使用第二个循环来销毁空适配器。
p.s. 我能编辑我的问题总结所有的帖子吗?我在StackOverflow上是新手...