C++ 用迭代器遍历vector并erase元素时的潜在问题
问题表述
-
在C++对,对于vector经常需要遍历其成员,然后按照某种规则把部分成员删除掉,这里就会用到erase(当然有其他方法,暂不赘述)。但是,如果要移除最后一个元素时,便要注意一些细节,不然便会产生野指针,导致程序segment fault,如下代码中的
IterVecNotSafe
函数,#include <iostream> #include <vector> using namespace std; void IterVecNotSafe() { vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8}; int cnt = 0; for (auto iter = vec.begin(); iter != vec.end(); ++iter) { if (*iter == 8) { vec.erase(iter); } std::cout << "not safe *iter = " << *iter << ", &iter = " << &*iter << ", ++iter addr: " << &*(iter + 1) << std::endl; if (cnt++ > 11) exit(EXIT_FAILURE); } } void IterVecSafe() { vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8}; for (auto iter = vec.begin(); iter != vec.end();) { // true可以换成所需要的condition if (*iter == 8) { iter = vec.erase(iter); } else { iter++; } // 迭代器取地址的方式&*iter 或者 std::addressof(*iter) std::cout << "safe *iter = " << *iter << ", &iter = " << &*iter << ", ++iter addr: " << &*(++iter) << std::endl; } } int main(int argc, char* argv[]) { IterVecNotSafe(); IterVecSafe(); return 0; }
- 程序输出为:
not safe *iter = 1, &iter = 0x1b83c20, ++iter addr: 0x1b83c24 not safe *iter = 2, &iter = 0x1b83c24, ++iter addr: 0x1b83c28 not safe *iter = 3, &iter = 0x1b83c28, ++iter addr: 0x1b83c2c not safe *iter = 4, &iter = 0x1b83c2c, ++iter addr: 0x1b83c30 not safe *iter = 5, &iter = 0x1b83c30, ++iter addr: 0x1b83c34 not safe *iter = 6, &iter = 0x1b83c34, ++iter addr: 0x1b83c38 not safe *iter = 7, &iter = 0x1b83c38, ++iter addr: 0x1b83c3c not safe *iter = 8, &iter = 0x1b83c3c, ++iter addr: 0x1b83c40 not safe *iter = 0, &iter = 0x1b83c40, ++iter addr: 0x1b83c44 not safe *iter = 0, &iter = 0x1b83c44, ++iter addr: 0x1b83c48 not safe *iter = 1041, &iter = 0x1b83c48, ++iter addr: 0x1b83c4c not safe *iter = 0, &iter = 0x1b83c4c, ++iter addr: 0x1b83c50 not safe *iter = 544501614, &iter = 0x1b83c50, ++iter addr: 0x1b83c54 safe *iter = 3, &iter = 0x1dfbc28, ++iter addr: 0x1dfbc28 safe *iter = 5, &iter = 0x1dfbc30, ++iter addr: 0x1dfbc30 safe *iter = 7, &iter = 0x1dfbc38, ++iter addr: 0x1dfbc38 safe *iter = 0, &iter = 0x1dfbc40, ++iter addr: 0x1dfbc40
- 可以发现,
IterVecNotSafe
函数删除最后一个元素后产生了野指针
- 程序输出为:
分析
-
vector::erase
官方说明erase
支持一次删除某个位置的元素,也支持删除一个区间的元素- 注意返回值的说明
- An iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence. Member type iterator is a random access iterator type that points to elements.
- 也就是,当删除最后一个元素时,新的迭代器将指向vector.end(),而对于vector来说,
end()
都是属于尾后迭代器(off-the-end iterator),指向的是尾元素的下一个位置,而这个位置根本不存在,只是一个标记而已;如果这时候再执行++iter
的操作,将导致错误,如输出结果所示
-
解决方法
- 常用的一种解决方案是如上面代码中的
IterVecSafe
函数,将erase后的结果再返回给迭代器,如此,当删除最后一个元素时,iter将触发iter==vec.end()
条件,然后终止循环
- 常用的一种解决方案是如上面代码中的