3

在粒子系统中,一旦粒子变得足够老,它们就需要死亡。由于它们存储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 并在 Visual Studio 2010 中编译后,我发现它不起作用:请参阅此处。正如答案本身所说,这不适用于关联容器。我发现这里最令人沮丧的是std::reverse_iterator并且std::iterator表现不同:

  • .erase不接受 areverse_iterator并且想要一个真实的东西(例如看到这个
  • rev_it.base()调用需要在擦除调用中递减
  • 擦除后,我需要将其转换std::iteratorstd::reverse_iterator

我曾想过使用向前std::iterator但向后迭代,这是一个糟糕的主意——但向后迭代的真正需要是确保循环不会跳过 erased 的相邻成员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()调用更具可读性。)

撇开括号不谈,我脑海中浮现的一句俄罗斯谚语是“一个被热牛奶烫伤的人在冷水上吹。”

4

3 回答 3

3

你的第二段代码很好。当我需要从正在迭代的列表中删除元素时,我也会这样做。

AFAIK 在需要时“手动”控制(即手动增加迭代器)没有任何问题。在你的情况下,它似乎是必要的。

我确信 iter 将通过利用 .erase() 函数的返回值来指向正确的下一个值,而且它对我来说似乎比 --iter.base() 调用更具可读性。

我完全同意。

编辑:正如@nm 在评论中所说,std::remove_if在您的情况下似乎已经足够了。

于 2013-07-05T07:36:52.057 回答
3

除了其他答案(尤其是juanchopanza的)之外,您还可以使用一个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 lambda 不可用,请随意使用自定义函子。)

这将起作用,因为可能的元素重复将发生对其进行评估并且已被删除的谓词之后,因此该谓词仍然只会为向量中的每个元素调用一次,而不是任何可能的重复。新的结束迭代器之后的值包含什么是完全不相关的,因为我们erase之后它们并且std::vector::erase根本不尝试delete任何东西。


编辑:当然,另一种选择是对粒子使用智能指针(特别是 C++11 的std::unique_ptrs,或者,如果您深入了解该主题并完全理解您在做什么,则std::shared_ptrs)。这至少可以让您无需手动管理他们的内存。在这种情况下,您可以直接将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>最终可能不能同样好地工作(但很可能你有一个很好的在这里使用指针的原因)。

于 2013-07-05T08:42:16.480 回答
2

我会使用两遍解决方案:

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());
于 2013-07-05T07:47:06.613 回答