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

循环迭代中安全移除std::list元素?代码报错解决方案

解决遍历std::list时安全移除游戏对象的问题

问题根源

当前代码的核心问题是在遍历m_game_object_list的过程中直接删除并擦除元素,导致正在使用的迭代器失效,后续访问已释放的内存(调试模式下表现为0xdddddddddddddddd标记)。具体出现在碰撞检测的嵌套循环中,调用item->onCollisionEnter(*item2)时可能触发destroyGameObject,直接操作列表造成迭代器失效。

另外,原有碰撞逻辑中嵌套遍历m_game_object_list属于冗余操作——已经通过view拿到了实体对象,完全没必要再遍历列表匹配UUID。


方案1:延迟删除(游戏开发通用推荐)

不立即删除对象,而是标记待销毁状态,在所有逻辑执行完成后统一清理,彻底避免遍历中修改列表的问题。

步骤1:给GameObject添加销毁标记

class GameObject {
    // ... 原有成员 ...
private:
    bool m_markedForDestruction = false;
public:
    bool isMarkedForDestruction() const { return m_markedForDestruction; }
    void markForDestruction() { m_markedForDestruction = true; }
};

步骤2:修改destroyGameObject为标记逻辑

void Engine::Scene::destroyGameObject(GameObject gameobject)
{
    auto uuid = gameobject.getUUID();
    for (auto obj : m_game_object_list)
    {
        if (obj->getUUID() == uuid)
        {
            obj->markForDestruction();
            break; // UUID唯一,找到即退出
        }
    }
}

步骤3:优化碰撞检测逻辑(移除冗余遍历)

// 替换原碰撞检测中的嵌套列表遍历代码
if (transform_1.position.x - x_offset_1 <= transform_2.position.x + x_offset_2 &&
    transform_1.position.y - y_offset_1 <= transform_2.position.y + y_offset_2 &&
    transform_1.position.x + x_offset_1 >= transform_2.position.x - x_offset_2 &&
    transform_1.position.y + y_offset_1 >= transform_2.position.y - y_offset_2)
{
    // 直接用已获取的game_object调用,无需遍历列表
    game_object_1.onCollisionEnter(game_object_2);
    // 如需双向触发碰撞回调,可添加:
    // game_object_2.onCollisionEnter(game_object_1);
}

步骤4:在update末尾统一清理待销毁对象

void Engine::Scene::update()
{
    // 原有碰撞检测、对象update逻辑...

    {
        for (auto& i : m_game_object_list)
        {
            if (!i->isMarkedForDestruction())
            {
                i->update();
            }
        }
    }

    // 统一清理标记销毁的对象
    for (auto it = m_game_object_list.begin(); it != m_game_object_list.end();)
    {
        if ((*it)->isMarkedForDestruction())
        {
            delete *it;
            it = m_game_object_list.erase(it);
        }
        else
        {
            ++it;
        }
    }
    
    renderScene();
}

方案2:迭代器安全遍历(适合必须立即删除的场景)

如果业务逻辑要求必须在遍历过程中立即删除对象,需避免使用范围for循环(隐式迭代器会失效),改用显式迭代器,并利用std::list::erase返回下一个有效迭代器的特性。同时提前复制待检测对象的UUID,避免遍历原列表时被修改影响。

辅助函数:通过UUID查找对象

GameObject* Engine::Scene::findGameObjectByUUID(UUID uuid)
{
    for (auto obj : m_game_object_list)
    {
        if (obj->getUUID() == uuid)
        {
            return obj;
        }
    }
    return nullptr;
}

修改碰撞检测逻辑

void Engine::Scene::update()
{
    {
        auto view = m_game_objects.view<Engine::RigidBody>();
        // 提前复制所有刚体对象的UUID,避免遍历原列表时被修改
        std::vector<UUID> rigidBodyUUIDs;
        for (auto i : view)
        {
            GameObject go = {i, this};
            rigidBodyUUIDs.push_back(go.getUUID());
        }

        // 遍历UUID列表,避免直接操作原对象列表
        for (size_t idx1 = 0; idx1 < rigidBodyUUIDs.size(); ++idx1)
        {
            GameObject* obj1 = findGameObjectByUUID(rigidBodyUUIDs[idx1]);
            if (!obj1) continue; // 对象已被删除,跳过

            GameObject game_object_1 = {obj1->getEntity(), this};
            auto& rigid_body_1 = game_object_1.getComponent<RigidBody>();
            auto& transform_1 = game_object_1.getComponent<Transform>();
            auto& box_collider_1 = game_object_1.getComponent<BoxCollider>();

            // 从idx1+1开始,避免重复检测两两碰撞
            for (size_t idx2 = idx1 + 1; idx2 < rigidBodyUUIDs.size(); ++idx2)
            {
                GameObject* obj2 = findGameObjectByUUID(rigidBodyUUIDs[idx2]);
                if (!obj2) continue; // 对象已被删除,跳过

                GameObject game_object_2 = {obj2->getEntity(), this};
                auto& rigid_body_2 = game_object_2.getComponent<RigidBody>();
                auto& transform_2 = game_object_2.getComponent<Transform>();
                auto& box_collider_2 = game_object_2.getComponent<BoxCollider>();

                if (game_object_1.getUUID() != game_object_2.getUUID() &&
                    (rigid_body_1.type != RigidBody::BodyType::Static || rigid_body_2.type != RigidBody::BodyType::Static))
                {
                    float x_offset_1 = box_collider_1.size.x / 2;
                    float y_offset_1 = box_collider_1.size.y / 2;

                    float x_offset_2 = box_collider_2.size.x / 2;
                    float y_offset_2 = box_collider_2.size.y / 2;

                    if (transform_1.position.x - x_offset_1 <= transform_2.position.x + x_offset_2 &&
                        transform_1.position.y - y_offset_1 <= transform_2.position.y + y_offset_2 &&
                        transform_1.position.x + x_offset_1 >= transform_2.position.x - x_offset_2 &&
                        transform_1.position.y + y_offset_1 >= transform_2.position.y - y_offset_2)
                    {
                        obj1->onCollisionEnter(*obj2);
                        // obj2->onCollisionEnter(*obj1);
                    }
                }
            }
        }
    }

    {
        for (auto& i : m_game_object_list)
        {
            i->update();
        }
    }
    
    renderScene();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 03:44:56