【问题标题】:Iterating over objects that may remove themselves from container迭代可能从容器中移除的对象
【发布时间】:2015-07-07 11:16:24
【问题描述】:

如果在迭代期间可能会删除对象,我如何安全地迭代容器?我基本上是在尝试实现观察者模式,有些事件会导致监听器从订阅容器中移除,从而导致迭代器出现问题。

我考虑过为每个听众提供一个标志,当它希望从订阅者列表中删除时,它可以设置该标志。因此,主体不会直接删除自己的侦听器,而是会在迭代期间检查标志,跳过标记的侦听器,并在发送完通知后清除所有标记的侦听器。

我考虑的另一个选择是为每次调用 listener.notify 启动一个线程,并让主题上的 remove_listener 方法成为互斥锁操作。

第一个选项似乎不好,因为使用标志来指示某些对象的状态往往会在整个程序中激增,并且很快每次处理对象时都需要检查 if (!object.TO_BE_DELETED) 类似于源自 null 的问题指针。

第二个选项带来了使用线程的所有麻烦,如果程序使用大量事件,我很确定这会很快产生性能问题。

那么有什么更好的办法解决这个问题呢?

【问题讨论】:

    标签: c++ iteration containers observer-pattern


    【解决方案1】:

    你不应该在从容器中删除一个元素后使用同一个迭代器,因为迭代器会失去意义,你应该拥有迭代器的更新版本。例如 std::list::erase 函数返回一个迭代器,指向被移除元素的下一个位置。

    【讨论】:

      【解决方案2】:

      没有迭代器:

      for(int i = v.size() - 1; i >= 0; i--) {
          if(shouldDelete(v[i]))
              v.erase(v.begin() + i);
      }
      

      使用迭代器:

      for(auto iter = v.begin(); iter != v.end(); ) {
          if(shouldDelete(*iter)) {
              iter = v.erase(iter);
          } else iter++;
      }
      

      在这种情况下,您不能使用 range-for 循环,因为删除元素会使所有迭代器无效,并且 range-for 循环在内部使用迭代器。

      请注意,我在这里假设向量;如果您使用的是列表或集合,则只有迭代器方法有效。 (这两种方法都应该适用于双端队列。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-12-18
        • 1970-01-01
        • 1970-01-01
        • 2011-11-22
        • 2018-08-01
        • 2013-06-17
        • 2017-08-15
        相关资源
        最近更新 更多