尝试擦除最后一个 std::vector 元素时程序崩溃
Program crashes when attempting to erase the last std::vector element
我正在遍历向量 std::vector<Bullet*> bullets
,我正在寻找与敌人的碰撞。它在任何情况下都很好用,除了以下情况:最后发射的子弹(必须不止一颗)与敌人相撞。
代码-
for(std::vector<Bullet*>::iterator it = bullets.begin(); it != bullets.end(); ++it)
{
if ((*it)->getSprite()->getGlobalBounds().intersects(enemy->getSprite()->getGlobalBounds()))
{
delete *it;
bullets.erase(it);
enemy->destroy();
if (bullets.size() == 0)
break;
}
}
我评论了 for
循环中的特定元素,发现 bullet.erase(it)
调用使程序崩溃。
当崩溃发生时,我收到一个 return 代码:134 (0x86)。该代码有什么问题?
(*it)->getSprite()
returns 指向来自 Bullet
class.
的精灵的指针
如何使用 remove_if
和 erase
组合:
auto is_hit = [&enemy](Bullet *bullet)
{
if (bullet->getSprite()->getGlobalBounds().intersects(enemy->getSprite()->getGlobalBounds()))
{
delete bullet;
enemy->destroy();
return true;
}
return false;
};
bullets.erase(std::remove_if(bullets.begin(), bullets.end(), is_hit), bullets.end());
供您考虑:
下面的代码片段展示了我如何从尾部清除矢量(使用 push_back() 向尾部添加元素的补充操作)
while(!gBoard.empty())
{
Cell_t* cell = gBoard.back(); // fetch last element (a ptr)
gBoard.pop_back(); // remove last element
delete cell; // remove cell from heap - raw pointer
}
也许您可以采用这种清理方式并使用多个向量...它仍然可能比其他方法更快。
在您的问题中,每颗子弹似乎至少有两个目的地……命中或未命中。
while ( ! Bullets.empty() ) // spin through bullet list
{
Bullet* aBullet = Bullets.back(); // fetch copy of last element
Bullets.pop_back(); // remove last element
if (*aBullet)-> getSprite()->getGlobalBounds().
intersects(enemy->getSprite()->getGlobalBounds()))
{
// HIT!
Hit.push_back(aBullet); // capture the element to Hit bucket
enemy->destroy(); // tbd - a decision? or always final?
// no delete
if (bullets.size() == 0) // no more to compute, redundant to while
break;
}
else
{
// MISS
Missed.push_back(aBullet); // capture element to Missed bucket
}
} // while
assert(bullets.empty()); // bullets have been consumed
// clean up spent bullets that intersected
while (! Hit.empty() )
{
Bullet* aBullet = Hit.back(); // copy last element from Hit
Hit.pop_back(); // remove last element from Hit
delete aBullet; // tbr - delete the dynamic memory
}
// clean up spent bullets that missed
// move the bullet from Missed vec back into Bullets vec
// for tbd - furthur evaluation ... did the bullet hit any other obj
// the following also happens to 'undo' the seq reversal
while (! Missed.empty() )
{
Bullets.push_back (Missed.back()); // copy last element from Missed
Missed.pop_back(); // remove last element from Missed
// tbd - also delete the missed bullet?
// or do you check for these bullets to collide with other objects
}
// possibly a copy can do this last loop, but this is simple and
// undoes the reversal.
and found out that the bullet.erase(it) call [for that last element]
crashes the program
从某种意义上说,您可能过早地执行了擦除。
考虑以下几点:
射程、目标和武器类型的测试参数可能会结合起来实现,例如,10% 的命中率。因此,在 1000 次射击的集合中,(1000 == bullets.size()),将有 (~) 100 颗子弹击中目标。
您的代码找到每个元素,并使用 bullets.erase() 在向量中创建 100 'holes'。因为矢量数据保持连续,所以 erase 方法还会移动其他元素来填充由 erase 创建的空洞。 (有关实现之间可能有何不同的详细信息。)
一般来说,100 次擦除会导致 100 次每次少于(最多)1000 个元素的洗牌……这种一次一个的方法可能是一个相对 'slow' 的过程。
作为当前设计的替代方案,而不是查找和擦除,推迟擦除直到您的代码识别并标记所有 'intersects'。
你可以用同样的方法找到相交(命中),但是'mark'它们,先不要擦掉它们。选项包括向 Bullet class 添加一个 bool,或者维护一个匹配的 bool 向量来为每个 bullet 保存此标志。
使用两个索引,
-- i1 初始化为 0(第一个(最左边)向量元素)和
-- i2 初始化为 (bullets.size() - 1) [最后(最右边)向量元素]
-- 自旋增量 i1 找到第一个命中,
-- 自旋递减 i2 找到最后一个未命中,
-- 然后 std::swap (bullets[i1], bullets[i2])
重复直到 i1 >= i2
现在所有命中都是连续的并且位于向量的尾部,对 100 个命中执行一次擦除
这应该可以消除任何洗牌。
此外,不应使用已擦除的元素...因为擦除发生在过程结束时。
我正在遍历向量 std::vector<Bullet*> bullets
,我正在寻找与敌人的碰撞。它在任何情况下都很好用,除了以下情况:最后发射的子弹(必须不止一颗)与敌人相撞。
代码-
for(std::vector<Bullet*>::iterator it = bullets.begin(); it != bullets.end(); ++it)
{
if ((*it)->getSprite()->getGlobalBounds().intersects(enemy->getSprite()->getGlobalBounds()))
{
delete *it;
bullets.erase(it);
enemy->destroy();
if (bullets.size() == 0)
break;
}
}
我评论了 for
循环中的特定元素,发现 bullet.erase(it)
调用使程序崩溃。
当崩溃发生时,我收到一个 return 代码:134 (0x86)。该代码有什么问题?
(*it)->getSprite()
returns 指向来自 Bullet
class.
如何使用 remove_if
和 erase
组合:
auto is_hit = [&enemy](Bullet *bullet)
{
if (bullet->getSprite()->getGlobalBounds().intersects(enemy->getSprite()->getGlobalBounds()))
{
delete bullet;
enemy->destroy();
return true;
}
return false;
};
bullets.erase(std::remove_if(bullets.begin(), bullets.end(), is_hit), bullets.end());
供您考虑:
下面的代码片段展示了我如何从尾部清除矢量(使用 push_back() 向尾部添加元素的补充操作)
while(!gBoard.empty())
{
Cell_t* cell = gBoard.back(); // fetch last element (a ptr)
gBoard.pop_back(); // remove last element
delete cell; // remove cell from heap - raw pointer
}
也许您可以采用这种清理方式并使用多个向量...它仍然可能比其他方法更快。
在您的问题中,每颗子弹似乎至少有两个目的地……命中或未命中。
while ( ! Bullets.empty() ) // spin through bullet list
{
Bullet* aBullet = Bullets.back(); // fetch copy of last element
Bullets.pop_back(); // remove last element
if (*aBullet)-> getSprite()->getGlobalBounds().
intersects(enemy->getSprite()->getGlobalBounds()))
{
// HIT!
Hit.push_back(aBullet); // capture the element to Hit bucket
enemy->destroy(); // tbd - a decision? or always final?
// no delete
if (bullets.size() == 0) // no more to compute, redundant to while
break;
}
else
{
// MISS
Missed.push_back(aBullet); // capture element to Missed bucket
}
} // while
assert(bullets.empty()); // bullets have been consumed
// clean up spent bullets that intersected
while (! Hit.empty() )
{
Bullet* aBullet = Hit.back(); // copy last element from Hit
Hit.pop_back(); // remove last element from Hit
delete aBullet; // tbr - delete the dynamic memory
}
// clean up spent bullets that missed
// move the bullet from Missed vec back into Bullets vec
// for tbd - furthur evaluation ... did the bullet hit any other obj
// the following also happens to 'undo' the seq reversal
while (! Missed.empty() )
{
Bullets.push_back (Missed.back()); // copy last element from Missed
Missed.pop_back(); // remove last element from Missed
// tbd - also delete the missed bullet?
// or do you check for these bullets to collide with other objects
}
// possibly a copy can do this last loop, but this is simple and
// undoes the reversal.
and found out that the bullet.erase(it) call [for that last element] crashes the program
从某种意义上说,您可能过早地执行了擦除。
考虑以下几点:
射程、目标和武器类型的测试参数可能会结合起来实现,例如,10% 的命中率。因此,在 1000 次射击的集合中,(1000 == bullets.size()),将有 (~) 100 颗子弹击中目标。
您的代码找到每个元素,并使用 bullets.erase() 在向量中创建 100 'holes'。因为矢量数据保持连续,所以 erase 方法还会移动其他元素来填充由 erase 创建的空洞。 (有关实现之间可能有何不同的详细信息。)
一般来说,100 次擦除会导致 100 次每次少于(最多)1000 个元素的洗牌……这种一次一个的方法可能是一个相对 'slow' 的过程。
作为当前设计的替代方案,而不是查找和擦除,推迟擦除直到您的代码识别并标记所有 'intersects'。
你可以用同样的方法找到相交(命中),但是'mark'它们,先不要擦掉它们。选项包括向 Bullet class 添加一个 bool,或者维护一个匹配的 bool 向量来为每个 bullet 保存此标志。
使用两个索引,
-- i1 初始化为 0(第一个(最左边)向量元素)和
-- i2 初始化为 (bullets.size() - 1) [最后(最右边)向量元素]
-- 自旋增量 i1 找到第一个命中,
-- 自旋递减 i2 找到最后一个未命中,
-- 然后 std::swap (bullets[i1], bullets[i2])
重复直到 i1 >= i2
现在所有命中都是连续的并且位于向量的尾部,对 100 个命中执行一次擦除
这应该可以消除任何洗牌。
此外,不应使用已擦除的元素...因为擦除发生在过程结束时。