在这种情况下如何擦除向量元素
How to erase an vector element in this situation?
我制作了一款游戏,兰博射击子弹,子弹击中僵尸,我想从僵尸向量中抹去被击中的僵尸。
这个嵌套循环逐个检查每个僵尸和子弹之间的碰撞。它工作得很好一段时间,但当我开始杀死更多,在某个时候,它崩溃了,因为它想使用擦除僵尸的功能。
for ( it = zombies.begin(); it != zombies.end(); ++it ) {
it->attack();
for (rambo.it = rambo.bullets.begin(); rambo.it != rambo.bullets.end(); ++rambo.it) {
if(checkBasicCollision(it,rambo.it) && it != zombies.end()){
zombies.erase(it);
}
}
}
我添加了it--;
后,zombies.erase(it);
工作得更好了,但它仍然有时崩溃。
我认为它的发生就像,例如有5个僵尸和20颗子弹,僵尸迭代器在第二个僵尸,第二个僵尸启动子弹循环来检查它是否被击中。循环开始,假设第三颗子弹击中僵尸,但循环仍在进行,即使僵尸被擦除,它仍继续循环。
我添加了break;经过zombies.erase(it);
后,现在没有任何问题。但是代码看起来太脏了。有没有其他方法可以轻松地删除当前元素
虽然提出了手动擦除的解决方案,但请注意它不是最惯用的解决方案。在惯用的c++中,您将在擦除-删除习语中使用std::remove_if
算法,如下所示:
// 1. A predicate that check whether a zombie was it by any bullet:
auto is_zombie_hit = [&rambo](Zombie const& zombie) {
auto is_bullet_hitting_zombie = [&zombie](Bullet const& bullet) {
return checkBasicCollision(zombie, bullet);
};
return std::any_of(
rambo.bullets.begin(),
rambo.bullets.end(),
is_bullet_hitting_zombie
);
};
// 2. Use the erase-remove idiom:
zombies.erase(
std::remove_if(zombies.begin(), zombies.end(), is_zombie_hit),
zombies.end()
);
注意:是的,你可以就地使用lambda,但是我更喜欢命名它们来指示它们的角色。
注意:这使用c++ 11,但是用谓词替换lambda是微不足道的,并且any_of
的实现很容易生成,就像all_of
和none_of
一样。
要使用erase
,您需要使用返回值并将其赋值给迭代器,以便在下一次迭代时有效。
for ( it = zombies.begin(); it != zombies.end(); ) {
it->attack();
for (rambo.it = rambo.bullets.begin(); rambo.it != rambo.bullets.end(); ++rambo.it) {
if(checkBasicCollision(it,rambo.it) && it != zombies.end()){
it = zombies.erase(it); // erase will increment the iterator
}
else{
++it; // no erase, increment the iterator manually
}
}
}
从vector::erase
的文档中返回值为:
指向被函数调用擦除的最后一个元素后面元素的新位置的迭代器。如果该操作删除了序列中的最后一个元素,则为容器端。
擦除vector元素时,迭代器和索引失效。此外,你的代码是不正确的情况下2或更多的子弹击中相同的僵尸(如果它是可能的)。因为用第二颗子弹内循环会试图消灭已经被击中的僵尸。相反,您应该这样做:
for ( uint i = 0; i < zombies.size(); ++i)
{
for( auto it = rambo.bullets.begin(); it != rambo.bullets.end(); ++it)
{
if(checkBasicCollision(zombies[i], it)
{
zombies.erase( zombies.begin() + i );
--i;
break; // zombie is dead (well, technically it was already dead)
// so no further checks are needed (i.e. exit inner loop)
}
}
}
可能是这样的:
auto zombie_tail = std::remove_if(zombies.begin(), zombies.end(), [&](Zombie const & zombie) {
zombie.attack();
return std::any_of(rambo.bullets.begin(), rambo.bullets.end(), [&](Bullet const & bullet) {
return checkBasicCollision(zombie, bullet);
});
});
zombies.erase(zombie_tail, zombies.end());
或者,如果你想远离c++算法:
for (it = zombies.begin(); it != zombies.end(); ) {
it->attack();
// Attempt to find a bullet that hit.
for(rambo.it = rambo.bullets.begin(); rambo.it != rambo.bullets.end(); ++rambo.it)
if (checkBasicCollision(it, rambo.it))
break;
// Possibly remove the zombie, and advance the iterator
if (rambo.it != rambo.bullets.end())
it = zombies.erase(it);
else
++it;
}
直接,易于阅读和掌握,但可能不是很花哨;
for ( auto& z : zombies )
z.attack();
for( auto& b : rambo.bullets )
{
auto itr = zombies.begin();
while( itr != zombies.end() )
{
if( checkBasicCollision(b,*itr) )
itr = zombies.erase(itr);
else
++itr;
}
}
checkBasicCollision
现在接受引用,而不是迭代器
- 为什么我的向量::擦除调用会抛出"vector subscript out of range"?
- 分割错误:向量中的擦除功能
- 如何使用擦除和迭代器来删除二维向量中的项目
- 从向量和对向量中擦除元素的差异
- 如何将某个元素从shared_ptr擦除到对象向量?
- 如何根据某些值擦除/删除类对象的向量
- 给定一个向量,擦除低于 itemsnum 的元素
- C++ 向量::使用类对象迭代器擦除不擦除向量成员
- C 矢量元素擦除与新向量创建
- 从向量中擦除元素是否也会擦除元素中存在的所有成员向量
- 向量擦除功能无法正常工作
- 使用迭代器时遇到问题,每 M 次传递一次擦除一个向量项
- 如何更有效地从向量或集合中擦除元素?
- 插入排序与插入/擦除?(所以我需要对一个向量进行排序,而不是手工做事,而是使用插入和擦除
- 使用擦除-删除范例将元素从一个向量移动到另一个向量
- 当我在c++中擦除向量时,我遇到了双自由错误
- 用算法擦除向量中的特定元素
- 在这种情况下如何擦除向量元素
- 如何擦除向量的一部分
- 如何查找和擦除向量元素中的某个字符