Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;迭代器;“删除证明”;_C++_Iterator_Observer Pattern - Fatal编程技术网

C++ C++;迭代器;“删除证明”;

C++ C++;迭代器;“删除证明”;,c++,iterator,observer-pattern,C++,Iterator,Observer Pattern,我不知道是否有我需要的特殊关键字。我正在写一篇基础文章,我关心一些问题。我的实现是经典的。我使用的是一组观察者,每当我需要触发一个事件时,我都会遍历这个集合并调用每个观察者的notify方法。我的问题如下。当可观察对象向观察者发送事件时会发生什么情况,如果: 一个观察者希望在事件期间从观察者集中移除自己(或任何其他观察者) 一个观察者想要清除观察者集(移除所有观察者) 一个观察者摧毁可观察的物体 我知道所有这些案件最终都会发生。我有第三个想法,但这是离题的。对于第一种和第二种情况,问题是删除

我不知道是否有我需要的特殊关键字。我正在写一篇基础文章,我关心一些问题。我的实现是经典的。我使用的是一组观察者,每当我需要触发一个事件时,我都会遍历这个集合并调用每个观察者的notify方法。我的问题如下。当可观察对象向观察者发送事件时会发生什么情况,如果:

  • 一个观察者希望在事件期间从观察者集中移除自己(或任何其他观察者)
  • 一个观察者想要清除观察者集(移除所有观察者)
  • 一个观察者摧毁可观察的物体
我知道所有这些案件最终都会发生。我有第三个想法,但这是离题的。对于第一种和第二种情况,问题是删除或清除std::set将使我用于枚举可观察项的迭代器无效。即使没有,被观察者也不应该通知任何在事件处理过程中被移除的观察者

我还没有找到一个集合的实现,它提供了一个迭代器,能够在删除任何项时保持有效。不过,实现它是可能的,代价是一些间接的操作,并在容器中存储对活动迭代器的任何引用,以便在必要时对其进行更新

另一个解决方案是复制观察者集,迭代该副本,并检查当前迭代的观察者是否仍在真集合中。(这将忘记在活动期间添加的任何新观察员,但我不在乎这种情况)


您对这个问题有什么建议/解决方案吗?

我假设您正在容器中存储指针或其他东西,而不是实际的观察者对象本身。因为你显然不能让观察者代码删除观察者对象

当需要在迭代器处于活动状态时修改集合时,就不能使用std::set。这不是它的目的

如果需要从容器中删除内容,可以尝试从事件例程返回一个值,该值告诉调用方(具有迭代器的代码)从容器中删除该观察者。代码可以删除迭代器指向的对象,并沿着序列正确地继续

如果需要添加内容,请不要直接将其添加到容器中。相反,将它们添加到队列中,并让触发事件的代码在完成对容器的迭代后添加新内容


如果容器有少量的对象,并且对象很小(指针),我可能只复制容器并在副本上迭代。这样,观察者就可以随心所欲地使用容器,而不会弄乱迭代器。

让观察者的通知方法接受指向该观察者的迭代器,并返回指向下一个观察者的迭代器:

// PSEUDO-CODE not to be taken literally.

class normalObserver : public Observer {
  iterator notify(iterator me) { 
    assert(*me == this); 
    // do stuff
    return ++me;
  }
 };

 class deleteMeObserver : public Observer {
   iterator notify(iterator me) {
    assert(*me == this); 
     // do stuff
     object.observers.erase(me++);
     return me;
   }
 };

 class deleteEveryObserver :public Observer {
   iterator notifiy(iterator me) {
    assert(*me == this); 
     // do stuff
     object.observers.clear();
     return object.observers.end();
   }
};

 class Object {
   set::set<Observer*> observers;
   void notifyObservers() {
     for(it = observers.begin(); it != observers.end(); ) {
       it = (*it)->notify(it);
     }
   }
 };
//伪代码不能按字面理解。
类正常观察者:公共观察者{
迭代器通知(迭代器me){
断言(*me==this);
//做事
还给我;
}
};
类deleteMeObserver:公共观察者{
迭代器通知(迭代器me){
断言(*me==this);
//做事
object.observators.erase(me++);
还我;
}
};
类deleteEveryObserver:公共观察者{
迭代器通知(迭代器me){
断言(*me==this);
//做事
object.observators.clear();
返回object.observators.end();
}
};
类对象{
集合:集合观察者;
作废{
for(it=observators.begin();it!=observators.end();){
it=(*it)->通知(it);
}
}
};

我建议使用
std::list
而不是
set
进行编译,因为这样迭代器在删除元素时不会失效,当然除非它删除了自身


您可以通过将指针(最好是智能指针,甚至是弱指针)保持指向观察者对象而不是迭代器来处理想要删除自身的观察者(这样,当它删除自身时,指针不会失效(只要它是智能的,并且不删除自身))

不要让观察者直接访问集合。让他们通过一个可以访问集合和迭代器的方法进行修改,在调用观察器之前,迭代器应该递增。这个方法可以检查迭代器中的项是否被删除,如果是,则增加它。对于全部删除的情况,只需将迭代器设置为
end()

,而不是让观察者删除自己,您可以让他们将自己标记为“todelete”当遇到这样标记的观察者时,让迭代的代码执行实际的删除。@StevenBurnap这带来了一个语义问题:删除是立即生效,还是在观察结束时生效?例如,如果Observer4删除了Observer6,Observer6还会收到当前事件的通知吗?啊……我没有仔细阅读,假设观察者只删除自己。@Robᵩ: 这一点很好,但通常的观察者模式不会以任何特定顺序通知观察者(事实上,他使用的是
set
而不是像
vector
这样的插入顺序容器,这也表明了这一点),这意味着处理删除的唯一可靠方法是在通知所有观察者之后。我的问题是当观察者删除可观察对象时。如果一个观测者删除了另一个观测者,我假设被删除的观测者有责任在他的破坏过程中将他自己从集合中删除。这又回到了第一个问题:当一个观察者从活动观察者集中移除另一个观察者时,该怎么办?实际上,
std::set
对于这个用例有很好的迭代器失效语义。唯一的问题是当您有一个迭代器指向要删除的项时。手术室