[英]How to erase an vector element in this situation?
我做了一個游戲,rambo射擊子彈和子彈擊中了僵屍,我想從僵屍矢量中刪除被擊中的僵屍。
這個嵌套循環逐個檢查每個僵屍和子彈之間的沖突。 它有效一段時間,但當我開始殺死更多時,它會崩潰,因為它想要使用已刪除僵屍的功能。
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個子彈,僵屍迭代器是第二個僵屍,第二個僵屍開始子彈循環來檢查它是否被擊中。 循環開始,讓第三個子彈擊中僵屍,但循環仍在進行,即使僵屍被刪除,它仍然繼續循環。
我加了休息; 在zombies.erase(it);
現在它沒有任何問題。 但代碼看起來很臟。 有沒有其他方法可以輕松擦除當前元素
雖然提出了手動擦除的解決方案,但請注意它不是最慣用的解決方案。 在慣用的C ++中,你可以在erase-remove習慣用法中使用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
的文檔中,返回值為:
一個迭代器,指向函數調用擦除的最后一個元素后面元素的新位置。 如果操作擦除序列中的最后一個元素,則這是容器結束。
也許是這樣的:
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;
}
擦除向量元素時,迭代器和索引將失效。 你的代碼也不正確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)
}
}
}
直接,易於閱讀和掌握,但也許不是很花哨;
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
現在接受引用,而不是迭代器
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.