擦除(和删除)时需要手动控制迭代器
Manual control of iterator when erasing (and deleting) is necessary
在粒子系统中,一旦粒子足够老,它们就需要死亡。由于它们存储在std::vector
中,因此在XCode中运行良好的方法是:
for(std::vector<Particle*>::reverse_iterator iter = particles.rbegin(); iter != particles.rend(); ++iter) {
(*iter)->update();
if ( (*iter)->isDead() ) {
delete (*iter);
particles.erase( --iter.base() );
}
}
在启动到Windows并在VisualStudio2010中编译后,我发现它不起作用:请参阅此处。正如答案本身所说,这对关联容器不起作用。我发现这里最令人沮丧的是std::reverse_iterator
和std::iterator
的行为不同:
.erase
不接受reverse_iterator
,并且想要真实的东西(例如,参见此)rev_it.base()
调用需要在擦除调用中递减- 擦除后,我需要将
std::iterator
转换为std::reverse_iterator
我曾想过使用前向std::iterator
,但向后迭代,这是一个糟糕的想法——但向后迭代的真正需要是确保循环不会跳过已擦除particles
的相邻成员。
不过,对我来说有意义的是,如果进行了.erase()
调用,就不迭代:
for( std::vector<Particle*>::iterator iter = particles.begin(); iter != particles.end(); ) {
(*iter)->update();
if ( (*iter)->isDead() ) {
delete (*iter);
iter = particles.erase(iter);
} else {
++iter;
}
}
这可以编译、工作,而且似乎不是问题。然而,问题是:
我是不是忽略了一些让这个想法变得特别愚蠢的东西
(我确信iter
会利用.erase()
函数的return
值指向正确的下一个值,而且对我来说,它似乎比--iter.base()
调用更可读。)
撇开这句话不谈,我脑海中浮现的一句俄语谚语是"被热牛奶烧伤的人会被冷水烫伤。"
除了其他答案(尤其是juancopanza的答案),您还可以使用单个std::remove_if
:
particles.erase(std::remove_if(particles.begin(), particles.end(),
[](Particle *particle) -> bool {
bool dead = p->isDead();
if(dead)
delete p;
return dead;
}),
particles.end());
(如果没有C++11 lambdas,请随意使用自定义函子。)
这将起作用,因为元素的可能重复将发生在为其评估谓词之后,并且它已经被删除,因此谓词仍然只对向量中的每个元素调用一次,而不会对任何可能的重复调用。新的结束迭代器之后的值包含什么是完全无关的,因为我们在之后erase
它们,而std::vector::erase
根本不尝试delete
。
编辑:当然,另一种选择是为粒子使用智能指针(特别是C++11的std::unique_ptr
s,或者,如果您对主题有很好的理解并完全理解您正在做的事情,则为std::shared_ptr
s)。这至少可以让你不需要手动管理他们的内存。在这种情况下,您可以直接将isDead
方法映射到谓词函数,而根本不需要lambda(并且您不需要修改谓词内部的范围,这仍然有点单一):
std::vector<std::unique_ptr<Particle>> particles;
...
particles.erase(std::remove_if(particles.begin(), particles.end(),
std::mem_fn(&Particle::isDead)),
particles.end());
编辑:尽管我们在做这件事的时候,我还是忍不住问你一个问题,这些粒子是否需要动态分配,std::vector<Particle>
最终是否可能不能同样好地工作(但很可能你有充分的理由在这里使用指针)。
您的第二段代码很好。当我需要从正在迭代的列表中删除元素时,我也会这样做。
AFAIK当需要时,"手动"控制(即手动递增迭代器)没有错。就你而言,这似乎是必要的。
我相信iter会通过取.ecrease()函数的返回值的优势,而且它似乎更比--iter.base()调用更可读。
我完全同意。
编辑:正如@n.m在评论中所说,std::remove_if在您的情况下似乎已经足够了。
我会使用两遍解决方案:
1) 删除元素并设置为NULL:
void killParticle(Particle*& p)
{
if ( p->isDead() ) {
delete p;
p = NULL;
}
std::for_each(particles.begin(), particles.end(), killParticle);
2) 使用擦除删除习惯用法删除NULL元素:
particles.erase(std::remove(particles.begin(), particles.end(), NULL),
particles.end());
- 使用std::multimap迭代器创建std::list
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++中带有List类的迭代器Segfault
- 如何在c++迭代器类型中包装std::chrono
- 集合上的输出迭代器:assign和increment迭代器
- Boost Spirit,获取迭代器内部语义动作
- 对于set上的循环-获取next元素迭代器
- 为什么output_editor Concept不需要output_e迭代器标记
- c++17文件系统::recursive_directory迭代器()在mac上没有给出这样的目录,但在windows上
- 使用迭代器时如何访问对象在向量中的位置?
- std::vector::迭代器是否可以合法地作为指针
- 跟随整数索引列表的自定义类迭代器
- 不明白迭代器,引用和指针失效,一个例子
- 我可以使用反向迭代器作为ForwardIt吗
- ESP8266单片机矢量迭代器的C++问题
- 如何在C++中将迭代器作为函数参数传递
- 是否应避免从非常量迭代器转换为常量迭代器?
- 如何在 c++ 中将字符串迭代器变量传递给函数?
- 为什么 vector 的随机访问迭代器给出与指针不同的内存地址?
- 擦除(和删除)时需要手动控制迭代器