迭代std :: vector(间接)时删除

xis*_*urg 4 c++ vector

这个问题已被多次询问,但我的情况略有不同.假设我有一个观察者的std :: vector,当某个事件发生时我会通知它:

void SomeClass::doThing() {
    // do things ...
    // notify observers
    for (auto* o : mObservers) {
        o->thingHappened();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果在thingHappened观察者的实现中调用一个方法SomeClass从观察者中删除自己怎么办?有哪些最佳方法可以解决这个问题?

一种可能性是mObservers在for循环之前复制并使用它,但额外的副本可能是浪费.

另一种可能性是在循环结束后将更改委托给要运行的数组,也许在循环开始之前设置一个锁(只是一个布尔值),并且在设置此锁定时,变换向量的方法委托自己调用之后当lock设置为false时可以完成循环(可以使用lambdas向量完成...非常麻烦).

Jus*_*tin 7

如果您可以控制签名thingHappened(),则可以更改它以返回bool指示是否应将其删除.然后,您可以删除所有返回的值true(或false;取决于您想要的语义).

对我们来说幸运的是,std::remove_ifstd::partition保证在该范围内每个对象只调用一次谓词.

void SomeClass::doThing() {
    // do things ...
    // notify observers
    auto newEnd = std::remove_if(mObservers.begin(), mObservers.end(), [](auto *o) {
        return o->thingHappened();
    });
    // assuming mObservers is a vector
    mObservers.erase(newEnd, mObservers.end());
}
Run Code Online (Sandbox Code Playgroud)

  • @xissburg这将是一个单独的问题 (6认同)

Nat*_*ica 5

解决此问题的一种方法是更改​​数据结构.与std::list在除去元件的唯一的迭代器/参考/无效指针到该元件.由于列表的其余部分保持不变,我们需要做的就是在处理当前元素之前获取下一个元素的迭代器.那看起来像

for (auto it = the_list.begin(); it != the_list.end();)
{
    auto next = std::next(it);
    it->call_the_possibly_removing_function();
    it = next;
}
Run Code Online (Sandbox Code Playgroud)

  • @Xirema它可能发生在非多线程代码中:如果一个观察者有另一个观察者的引用,它可以调用一个成员函数来删除另一个 (2认同)