C++遍历删除std::vector元素时注释指定行引发崩溃的原因
问题描述
完成LeetCode字母异位词分组题目时,下述代码可正常运行,但注释掉代码中标注的break行后程序会崩溃,初步判断问题与std::vector的遍历、删除操作相关。
class Solution { public: vector<vector<string>> groupAnagrams(vector<string>& strs) { vector<vector<string>> ans; while (strs.size() > 0) { string tmp1 = strs[0]; vector<string> tmpAns{ tmp1 }; strs.erase(strs.begin()); sort(tmp1.begin(), tmp1.end()); auto itr = strs.begin(); while (itr != strs.end()) { string tmp2 = *itr; sort(tmp2.begin(), tmp2.end()); if (tmp2 == tmp1) { tmpAns.push_back(*itr); strs.erase(itr); if (strs.size() <= 0) break; // 注释掉这行就会崩溃 } else { ++itr; } } ans.push_back(tmpAns); } return ans; } }; int main() { Solution s; vector<string> in= vector<string>{"a", "a"}; vector<vector<string>> ans = s.groupAnagrams(in); std::cout << "Hello World!\n"; }
崩溃根本原因
核心问题是错误使用了std::vectorerase操作后的失效迭代器:
std::vector是连续内存存储的容器,调用erase(iterator pos)删除元素时,传入的pos迭代器会直接失效,同时该函数会返回被删除元素下一个位置的有效迭代器。- 代码里执行
strs.erase(itr)后,没有接收返回的有效迭代器,旧的itr已经变成野迭代器,对野迭代器做任何操作(包括和strs.end()比较、解引用)都是未定义行为,必然可能触发崩溃。
用测试用例{"a","a"}走一遍流程就能复现崩溃:
- 外层循环首次进入时,
strs长度为2,取出第一个"a"存入临时结果,删除首元素后strs仅剩1个"a"。 - 内层循环的
itr指向剩下的那个"a",排序比对后匹配异位词,执行strs.erase(itr),此时strs长度变为0,itr彻底失效。 - 如果没有那行
break,代码会回到内层循环的判断条件itr != strs.end(),此时用失效的itr和end()做比较,直接触发崩溃。
注意:那行判断size后break的代码只是刚好在「删除最后一个元素后strs为空」的场景下,跳过了后续使用失效迭代器的逻辑,属于歪打正着的补丁,没有从根上解决迭代器失效问题。如果遇到删除非末尾元素的场景,就算保留这行break,后续循环依然会使用失效迭代器,崩溃概率极高。
正确修复方案
删除那行临时补丁的break,在erase元素时直接接收函数返回的有效迭代器即可,修改后的内层循环代码如下:
auto itr = strs.begin(); while (itr != strs.end()) { string tmp2 = *itr; sort(tmp2.begin(), tmp2.end()); if (tmp2 == tmp1) { tmpAns.push_back(*itr); itr = strs.erase(itr); // 接收erase返回的下一个位置的有效迭代器 } else { ++itr; } }
这种写法完全符合STL容器迭代器的使用规范,不管删除的是哪个位置的元素,迭代器始终保持有效,不会触发未定义行为。
内容的提问来源于stack exchange,提问作者Charlie
相关产品推荐
相关产品推荐

