遍历可能从容器中移除的对象

Iterating over objects that may remove themselves from container

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

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

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

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

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

那么什么是更好的解决这个问题的方法呢?

从容器中擦除元素后永远不要使用同一个迭代器,因为迭代器会失去对您应该拥有迭代器更新版本的感觉。例如 std::list::erase 函数 return 一个迭代器指向被移除元素的下一个位置。

没有迭代器:

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 循环在内部使用迭代器。

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