You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SFML开发太空侵略者:容器元素超1个时碰撞检测异常崩溃

问题根因

崩溃完全由迭代器非法使用、对象生命周期管理错误导致,具体有两点:

  • 你在Bullet类的成员函数里执行bullets.erase(j)删除当前子弹对象时,会直接销毁this指针指向的当前实例。erase操作完成后,当前正在执行的hit函数属于已经被销毁的对象,后续所有对this的访问(包括外层敌人循环的下一轮迭代、当前作用域剩余代码的执行)都会访问已释放的内存,触发未定义行为。容器里只有1颗子弹时,erase后剩余逻辑刚好没有触发非法内存访问所以没崩溃,子弹数≥2时vector内存布局变化,就直接触发致命错误退出。
  • 你遍历vector擦除元素的写法本身存在迭代器失效风险:vector的erase操作会让被擦除位置及之后的所有迭代器全部失效,虽然你擦除后立刻break没有继续用失效迭代器,但额外嵌套一层循环靠ID匹配找元素的写法完全多余,既拉低效率也容易埋bug。
修复方法

核心原则是:不要在碰撞检测逻辑中途销毁当前正在执行成员函数的子弹对象,先完成所有碰撞判断、标记需要删除的元素,确认不会再访问对象成员后再执行容器擦除操作。
修复后的参考代码如下:

void Bullet::hit(vector<Enemy>& enemies,vector<Pixel>& oneShield, vector<Bullet>& bullets, int &killedEnemies, int shotsFired) {
    bool bulletHit = false;
    // 遍历敌人做碰撞检测,命中就直接删除敌人,用erase返回的有效迭代器避免失效
    for (auto enemyIt = enemies.begin(); enemyIt != enemies.end(); ) {
        if (enemyIt->shape.getGlobalBounds().intersects(this->shape.getGlobalBounds())) {
            enemyIt = enemies.erase(enemyIt);
            killedEnemies += 1;
            bulletHit = true;
        } else {
            ++enemyIt;
        }
    }
    // 确认子弹命中后,最后再删除当前子弹,删完立刻返回,不再访问已销毁的对象成员
    if (bulletHit) {
        for (auto bulletIt = bullets.begin(); bulletIt != bullets.end(); ) {
            if (bulletIt->ID == this->ID) {
                bullets.erase(bulletIt);
                break;
            }
            ++bulletIt;
        }
        return;
    }
}

额外注意事项:

  • 遍历vector擦除元素时,循环判断条件要用it != 容器.end(),不要用<比较迭代器;擦除元素后要接erase返回的下一个有效迭代器,不要对已经失效的迭代器做递增操作。
  • 只要对象被从容器中erase,指向该对象的指针、引用、迭代器就全部失效,绝对不能再通过这些失效值访问对象成员。
  • 原代码中命中敌人后再从头遍历整个敌人容器找同ID对象的逻辑完全冗余,第一次遍历到碰撞对象时直接处理即可,不需要二次遍历。

内容的提问来源于stack exchange,提问作者Dawid DXR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 00:57:25