本博客主要是分析在iterator遍历的过程中删除元素之后如何继续遍历的问题。
问题描述
我们在写代码的过程中,总是会碰到这样的场景,即在遍历一个array
或者list
的时候,如果碰到某个满足条件的元素就删除之,然后继续遍历。对于这种场景我们在c++的stl中经常会写出如下的代码,以list
为例子
list<int> ls;
ls.push_back(1);
ls.push_back(2);
ls.push_back(3);
for (auto it = ls.begin(); it != ls.end(); it++) {
if (*it == 2) {
ls.erase(it);
}
}
这种代码其实会出现严重的错误,因此我这边简要说明下,这种写法为什么会出现问题,以及正确的写法。(正确的写法在网上都能找到,我这边主要是对其为什么需要这样做进行补充)。
问题分析
list
中的erase
如图所示,list
当中存放着元素0, 1, 2, 99, 3, 4
,当我们调用erase
来删除元素1
的时候,此时ite
指向的元素1
被链表移除,因此ite++
不再指向链表中的下一个元素,而是一个不确定的量,所以我们要记住erase
函数的入参不能够被后续操作。
list
中的erase
源码
从源码中我们可以看出,我们首先获取position
的next_node
和prev_node
,然后分别修改next_node
和prev_node
的prev
和next
指针,将position
从链表中删除,最后返回next_node
的iterator
,因此我们可以修改遍历为如下:
list<int> ls;
ls.push_back(1);
ls.push_back(2);
ls.push_back(3);
for (auto it = ls.begin(); it != ls.end(); ) {
if (*it == 2) {
it = ls.erase(it);
} else {
it++;
}
}
vector
中的erase
对于vector
,其erase
操作也是类似的,源码中,直接将position + 1
到end
部分的元素copy到position
位置。对于vector
来说,其iterator
是普通指针,position
指的就是数组的位置,因此在erase
之后,我们只需要返回当前的数组位置就可以了。