尝试擦除最后一个std :: vector元素时程序崩溃

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尝试擦除最后一个std :: vector元素时程序崩溃相关的知识,希望对你有一定的参考价值。

我正在迭代矢量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)调用崩溃了程序。发生崩溃时,我收到一个返回码:134(0x86)。这段代码有什么问题?

(*it)->getSprite()返回一个指向Bullet类精灵的指针。

答案

并发现bullet.erase(it)调用[for last element]使程序崩溃

从某种意义上说,你可能过早地进行了擦除。

考虑以下:

范围,目标和武器类型的测试参数可能结合起来以实现例如10%的命中率。因此,在1000次射击的集合中,(1000 == bullets.size()),会有(〜)100颗子弹击中目标。

您的代码找到每个元素,并使用bullets.erase()在向量中创建100个“洞”。因为矢量数据是连续的,所以擦除方法还移动其他元素以填充由擦除创建的孔。 (具体实现方式可能有所不同。)

通常,100次擦除每次导致100次洗牌少于(最多)1000个元素......这种一次一个接近的方法可能是一个相对“慢”的过程。


作为当前设计的替代方案,而不是查找和擦除,推迟擦除直到您的代码识别并标记所有“交叉”。

  • 你可以用相同的方式找到相交(点击),但是“标记”它们,不要删除它们。选项包括向Bullet类添加bool,或者维护匹配的bool向量以保存每个项目符号的该标志。
  • 使用两个索引, - i1初始化为0(第一个(最左边)向量元素)和 - i2初始化为(bullets.size() - 1)[最后(最右边)矢量元素]

- 旋转增量i1找到第一个命中,

- 旋转递减i2以找到最后一个未命中,

- 然后std :: swap(子弹[i1],子弹[i2])

重复直到i1> = i2

现在所有命中都是连续的并且在向量的尾部,执行100次命中的单次擦除

这应该消除任何改组。

此外,不应使用擦除元素...因为擦除发生在过程结束时。

另一答案

使用remove_iferase组合怎么样:

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.

以上是关于尝试擦除最后一个std :: vector元素时程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

从 std::vector 中删除多个对象?

根据向量大小在 for 循环内擦除 std::vector 的索引

std::vector.erase() 只擦除它应该擦除的一半

std::string 擦除最后一个字符失败?

在 std::vector 中擦除、调试、发布

为啥在 std::vector 擦除中需要 begin()?