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

List iterator not incrementable断言问题:碰撞检测迭代删元素致异常

分析与解决「List iterator not incrementable」断言错误

问题现象

运行时触发List iterator not incrementable断言错误。

背景代码

DoCollisions是每帧遍历ColliderList检测碰撞的回调函数:

void Collision::DoCollisions(Game *game){ 
    for (ColliderList::const_iterator colliderAIt = colliders_.begin(); colliderAIt != colliders_.end(); colliderAIt++) { 
        ColliderList::const_iterator colliderBIt = colliderAIt; 
        for (++colliderBIt; colliderBIt != colliders_.end(); ++colliderBIt) { 
            Collider *colliderA = *colliderAIt; 
            Collider *colliderB = *colliderBIt; 
            if (CollisionTest(colliderA, colliderB)) { 
                game->DoCollision(colliderA->entity, colliderB->entity); 
            } 
        } 
    } 
}

当碰撞检测通过时,游戏实体的析构函数会调用DestroyCollider从colliders_列表中移除对应碰撞器:

void Collision::DestroyCollider(Collider *collider){ 
    colliders_.remove_if(std::bind1st((std::equal_to<Collider *>()), collider)); 
    delete collider; 
}

错误原因

这个断言错误的核心是迭代器失效:

  • 在DoCollisions的嵌套循环中,你正通过colliderAIt和colliderBIt遍历colliders_列表。
  • 当CollisionTest返回true时,game->DoCollision会触发实体析构,进而调用DestroyCollider使用remove_if从colliders_中删除目标碰撞器。
  • 对于std::list来说,一旦某个元素被移除,指向该元素的迭代器就会立即失效。如果此时循环尝试对失效的迭代器执行++操作(比如colliderAIt++或colliderBIt++),就会触发List iterator not incrementable的断言错误。

举个具体场景:假设当前colliderAIt指向的碰撞器在碰撞处理中被销毁,那么下一次循环执行colliderAIt++时,这个迭代器已经失效,直接触发断言。

解决方案

这里提供两种常用的可靠解决思路:

方案1:延迟移除碰撞器

不要在碰撞遍历过程中直接修改colliders_列表,而是先记录需要销毁的碰撞器,等遍历完成后再统一处理:

  1. 在Collision类中添加一个临时容器,用于存储待销毁的碰撞器:
    std::vector<Collider*> collidersToDestroy_;
    
  2. 修改DestroyCollider,把要销毁的碰撞器加入临时列表,而非立即移除:
    void Collision::DestroyCollider(Collider *collider){ 
        collidersToDestroy_.push_back(collider); 
    }
    
  3. 在DoCollisions遍历完成后,统一处理待销毁的碰撞器:
    void Collision::DoCollisions(Game *game){ 
        // 原有的碰撞检测遍历逻辑...
        
        // 遍历结束后处理销毁
        for (Collider* collider : collidersToDestroy_) {
            colliders_.remove_if([collider](Collider* c) { return c == collider; });
            delete collider;
        }
        collidersToDestroy_.clear();
    }
    

这种方法彻底避免了遍历过程中修改列表导致的迭代器失效问题,是最安全的做法之一。

方案2:使用安全的迭代器遍历方式

如果你不想延迟销毁,可以在遍历前提前保存下一个迭代器,这样即使当前迭代器指向的元素被移除,我们依然可以通过保存的下一个迭代器继续循环:

void Collision::DoCollisions(Game *game){ 
    for (auto colliderAIt = colliders_.begin(); colliderAIt != colliders_.end(); ) { 
        // 提前保存下一个迭代器
        auto nextColliderA = std::next(colliderAIt);
        
        auto colliderBIt = std::next(colliderAIt);
        for (; colliderBIt != colliders_.end(); ) { 
            // 提前保存B的下一个迭代器
            auto nextColliderB = std::next(colliderBIt);
            
            Collider *colliderA = *colliderAIt; 
            Collider *colliderB = *colliderBIt; 
            if (CollisionTest(colliderA, colliderB)) { 
                game->DoCollision(colliderA->entity, colliderB->entity); 
            } 
            
            // 使用保存的下一个迭代器推进循环
            colliderBIt = nextColliderB;
        } 
        
        // 使用保存的下一个迭代器推进循环
        colliderAIt = nextColliderA;
    } 
}

同时,建议把DestroyCollider中的旧风格bind1st替换为C++11+的Lambda表达式,可读性更好:

void Collision::DestroyCollider(Collider *collider){ 
    colliders_.remove_if([collider](Collider* c) { return c == collider; }); 
    delete collider; 
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:21:02