循环迭代中安全移除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
相关产品推荐
相关产品推荐

