STL容器根据迭代器的失效问题,其实可以分为两类容器:
(1)数组型容器的插入删除操作:vector、string、deque(均为顺序存储)
由于这类容器的插入或删除都会使所有迭代器失效,因此每次插入删除后都需要重新定位
(2)结点型数据容器的插入删除操作:list(使用链表存储)、map(使用红黑树存储)、set(使用红黑树存储)
由于这类容器删除时只会失效当前迭代器,而插入时不会使任何迭代器失效, 因此插入时不需重新定位,但是删除时需重新定位
一、遍历删除
为追求操作统一,两类容器在删除元素时每次都重新定位,即:
每次条件满足需要删除时,获取删除后返回的迭代器,而返回的迭代器直接指向了下一个位置,因此迭代器不用另外自增操作。
对于不满足条件时,需要迭代器另外自增操作,以保证循环正常进行,否则将进入死循环。
//vector示例第一类容器
vector<string> vec_str={"hello","hi","world"};
auto it=vec_str.begin();
while(it!=vec_str.end()){
if(it->size()==5)//指定位置删除示例
it=vec_str.erase(it);//返回的it自动指向下一个位置
else
it++;//未删除应移到下一个位置,否则陷入死循环
}
for(auto e:vec_str)
cout<<e<<endl;
vec_str.clear();
//map示例第二类容器
map<int,int> map_values;
for(int i=0;i<5;i++){
map_values.insert(pair<int,int>(i,i));
}
auto iter=map_values.begin();
while(iter!=map_values.end()){
if((iter->first)%2==0){//指定位置删除示例
iter=map_values.erase(iter);//返回的it指向下一个位置
}
else
iter++;
}
for(auto e:map_values){
cout<<e.first<<" "<<e.second<<endl;
}
map_values.clear();
二、遍历插入
首先第二类容器在插入时不会使任何迭代器失效,因此循环插入时不存在问题。
但是第一类容器存在迭代器失效问题,由于vector和list容器的插入操作都是前插,并且返回指向插入后的元素的迭代器,如果每次重新定位到下一个元素,需要向前进两个位置!因此每次重新定位比较麻烦,并且vector插入效率低下,使用list替换vector,避开迭代器失效问题是最佳选择。
注意不要妄想使用insert向map或set的指定位置插入元素,map和set都是有序容器(底层是RBT),因此插入后map会自动按key排序元素,而set会自动排序元素,因此遍历时向指定位置(迭代器位置)插入的需求不会发生在map,如果发生了请考虑重新选择容器。
//vector示例第一类容器(list作为中间容器示例第二类容器)
vec_str.push_back("hello");
vec_str.push_back("hi");
vec_str.push_back("world");
list<string> list_str;
list_str.resize(vec_str.size());//resize必须,否则copy越界
copy(vec_str.begin(),vec_str.end(),list_str.begin());
for(auto it1=list_str.begin();it1!=list_str.end();it1++){
if(it1->size()==5)
list_str.insert(it1,"test");//直接插入,迭代器不会失效
}
vec_str.resize(list_str.size());//resize必须,list_str大小已变,否则copy越界
copy(list_str.begin(),list_str.end(),vec_str.begin());
for(auto e:vec_str){
cout<<e<<endl;
}