首发于 C++与底层
STL利用迭代器iter遍历删除的bug(插入类似)

STL利用迭代器iter遍历删除的bug(插入类似)


部分参考:

c++ vector list map在遍历中删除元素_m4vsak123的专栏-CSDN博客

stl vector/list如何一边遍历一边删除_Coder-CSDN博客

为什么会遍历删除失败?
// 遍历1
auto tb = lll.begin(), te= lll.end();
for(;tb!=te;){
    lll.erase(tb);
    ++tb;
// 遍历2
for(;tb!=lll.end();){
    lll.erase(tb);
    ++tb;
}

两种遍历方法有什么区别?遍历2每次都会请求end的位置,而遍历1只请求一次然后缓存,如果使用vector遍历的同时删除,那么就会导致后续的vector迭代器失效( vector删除元素会把后面的元素整体迁移填补前面的缺失),比如之前缓存的end(),但是List底层是链表,反而幸免于难。

  • 一个具体一点的例子:
#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<int>d{1,2,3,4};
    for(vector<int>::iterator it=d.begin();it!=d.end();it++)
       d.erase(it);
    for(auto i:d){
        cout<<i<<endl;  // 2,4
}

有意思的是:如果是奇数会导致Segmentation fault,因为iter删除填补后会跳过end导致越界。

#include<bits/stdc++.h>
using namespace std;
int main(){
    vector<int>d{1,2,3};
    for(vector<int>::iterator it=d.begin();it!=d.end();it++)
       d.erase(it);
    for(auto i:d){
        cout<<i<<endl;  // 2,4
}

STL遍历删除标准写法

在你调用erase方法删除元素时,erase方法会返回下一个元素的迭代器,利用这一点,可以写这样的代码:

  • vector+list+map的写法
for(vector<int>::iterator it=d.begin();it!=d.end(); )
        if(*it==3)
            it=d.erase(it);
            it++;
}

map还能这样写:

for(vector<int>::iterator it=d.begin();it!=d.end(); )
        if(*it==3)
            d.erase(it++);