遍历std::list并删除元素时触发错误的原因与解决方法
std::list迭代时空指针解引用错误的原因与修复
问题代码
std::list<GameMatch*> games_; void GameMatchManager::ClearEndedGames() { if(games_.empty()) return; auto it = games_.begin(); while (it != games_.end()) { auto game = *it; if(game->MatchEnded()) { games_.erase(it); game->Destroy(); } ++it; } } void GameMatch::Destroy() { std::cout << "Destoying " << GetMatchId() << std::endl; delete this; }
遇到的错误

疑问:开头已经判断if(games_.empty()) return;,为什么还会触发空指针错误?这个判断不该阻止对空列表的迭代吗?
错误原因
- 迭代器失效导致非法访问
调用games_.erase(it)后,当前迭代器it会直接失效。如果此时删除的是列表最后一个元素,erase后列表变为空,后续执行++it会让迭代器指向非法内存区域,循环条件it != games_.end()检查时就会触发空指针解引用错误。 - 开头的
empty()判断仅在函数执行初期生效,迭代过程中列表可能被清空,这个判断无法覆盖迭代中的状态变化。
修复方案
方案一:利用erase的返回值更新迭代器(C++11+)
std::list::erase会返回指向被删除元素下一个位置的有效迭代器,用这个值更新迭代器即可避免失效问题:
void GameMatchManager::ClearEndedGames() { auto it = games_.begin(); while (it != games_.end()) { auto game = *it; if(game->MatchEnded()) { it = games_.erase(it); // 用erase返回的有效迭代器替换失效的it game->Destroy(); } else { ++it; // 仅当未删除元素时才递增迭代器 } } }
此时开头的empty()判断可以直接移除——当列表为空时,begin()等于end(),循环会直接跳过,逻辑更简洁。
方案二:使用std::remove_if简化实现
借助STL算法remove_if可以更简洁地处理元素删除,内部会自动维护迭代器有效性:
#include <algorithm> // 需要包含algorithm头文件 void GameMatchManager::ClearEndedGames() { games_.remove_if([](GameMatch* game) { if (game->MatchEnded()) { game->Destroy(); return true; // 返回true表示该元素需要被移除 } return false; }); }
这种写法更符合STL的设计思想,代码可读性和安全性更高。
内容的提问来源于stack exchange,提问作者Venelin
相关产品推荐
相关产品推荐

