从事件的迭代列表中删除元素

Delete element from a iterating list from an event

本文关键字:删除 元素 列表 迭代 事件      更新时间:2023-10-16

想知道从事件循环中不断迭代的列表中删除元素的最佳方法是什么。我有一个事件循环,我的列表正在更新

for (auto pIt = this->particles.begin(); pIt != this->particles.end(); pIt++) {
    (*pIt)->Update(system, gameTime);
}

在某个基于事件的时间,我必须从列表中删除一个随机元素。它在单独的函数中处理,使用:

this->particles.remove_if([particle](Particle *ptc)->bool {
    return ptc == particle;
});

这会导致列表迭代运行时错误,因为循环中的迭代器变得无效。解决这种情况的最佳方法是什么?

我假设元素的删除发生在Update()函数内部,并且"列表"实际上是std::list而不是std::vector

不要在循环标头中执行pIt++。相反,请在呼叫Update()之前pIt++。 对返回的原始对象调用Update()

for (auto pIt = this->particles.begin(); pIt != this->particles.end(); ) {
    auto pForCall = *(pIt++);
    pForCall->Update(system, gameTime);
}

这不起作用的另一种情况是,当要删除的元素不是我们调用Update的元素时。

擦除函数必须有权访问循环中使用的迭代器(使其成为成员变量(,如果该迭代器指向要删除的元素,则递增迭代器。这样,您可以防止循环迭代器因擦除而失效。

例:

#include <iostream>
#include <list>
struct Explosion;
struct Particle {
    void update(Explosion&);
};
struct Explosion {
    std::list<Particle> particles;
    std::list<Particle>::iterator particle_iter;
    void update() {
        for(particle_iter = particles.begin(); particle_iter != particles.end(); ) {
            auto to_call = particle_iter++;
            to_call->update(*this);
        }
    }
    template<class Condition>
    void filter(Condition condition) {
        for(auto i = particles.begin(); i != particles.end();) {
            auto to_remove = i++;
            if(condition(*to_remove)) {
                // Remove the element.
                if(particle_iter == to_remove)
                    particle_iter = i;
                particles.erase(to_remove);
            }
        }
    }
};
void Particle::update(Explosion& explosion) {
    explosion.filter([this](Particle& other) { return this == &other; });
    // The above ends up destroying this object.
    // Cannot no longer access this and should only return.
}
int main() {
    Explosion explosion{{Particle{}, Particle{}}, {}};
    std::cout << explosion.particles.size() << 'n';
    explosion.update();
    std::cout << explosion.particles.size() << 'n';
}