1.错误的map删除操作
假设有个map容器,用于存储大学班级中各个家乡省份对应的学生数,key为省份中文全拼,value为学生数。现需要删除人数为0的记录,删除代码如下:
map<string,int > countMap;for(map<string,int>::iterator it=countMap.begin();it!=countMap.end();++it){if(it->second==0){ countMap.erase(it); }}
猛一看,没问题,仔细一看,有巨坑,STL容器的删除和插入操作隐藏的陷阱主要有如下两条。
(1)对于节点式容器(map, list, set)元素的删除,插入操作会导致指向该元素的迭代器失效,其他元素迭代器不受影响;
(2)对于顺序式容器(vector,string,deque)元素的删除、插入操作会导致指向该元素以及后面的元素的迭代器失效。
所以,在删除一个元素的时候,是没有什么问题的。即:
for(map<string,int>::iterator it=countMap.begin();it!=countMap.end();++it){ if(it->second==0) { countMap.erase(it); break; }}
但是,当删除多个元素时,程序会出现崩溃。原因是通过迭代器删除指定的元素时,指向那个元素的迭代器将失效,如果再次对失效的迭代器进行++操作,则会带来未定义行为,程序崩溃。解决方法有二,还是以上面的map容器为例,示例删除操作的正确实现:
方法一:当删除特定值的元素时,删除元素前保存当前被删除元素的下一个元素的迭代器。
map<string,int >::iterator nextIt=countMap.begin();for(map<string,int>::iterator it=countMap.begin();;){ if(nextIt!=countMap.end()) { ++nextIt; } else { break; } if(it->second==0) { countMap.erase(it); } it=nextIt;}
如何更加简洁的实现该方法呢?下面给出该方法的《Effective STL》一书的具体实现:
for(map<string,int>::iterator it=countMap.begin();it!=countMap.end();){ if(it->second==0) { countMap.erase(it++); } else { ++it; }}
该实现方式利用了后置++操作符的特性,在erase操作之前,迭代器已经指向了下一个元素。
再者map.erase()返回指向紧接着被删除元素的下一个元素的迭代器,所以可以实现如下:
for(map<string,int>::iterator it=countMap.begin();it!=countMap.end();){ if(it->second==0) { it=countMap.erase(it); } else { ++it; }}
方法二:当删除满足某些条件的元素,可以使用remove_copy_if & swap方法。先通过函数模板remove_copy_if 按照条件拷贝(copy)需要的元素到临时容器中,剩下未被拷贝的元素就相当于被“删除(remove)”了,然后在将两个容器中的元素交换(swap)即可,可以直接调用map的成员函数swap。参考代码:
#include <iostream>#include <string>#include <map>#include <algorithm>#include <iterator> using namespace std;map<string,int> mapCount;//不拷贝的条件bool notCopy(pair<string,int> key_value){ return key_value.second==0;}int main(){ mapCount.insert(make_pair("tanwan",0)); mapCount.insert(make_pair("anhui",1)); mapCount.insert(make_pair("shanghai",0)); mapCount.insert(make_pair("shandong",1)); map<string,int> mapCountTemp;//临时map容器 //之所以要用迭代器适配器inserter函数模板是因为通过调用insert()成员函数来插入元素,并由用户指定插入位置 remove_copy_if(mapCount.begin(),mapCount.end(),inserter(mapCountTemp,mapCountTemp.begin()),notCopy); mapCo<a style="color:transparent">本文来源gao($daima.com搞@代@#码$网</a>unt.swap(mapCountTemp);//实现两个容器的交换 cout<<mapCount.size()<<endl; //输出2 cout<<mapCountTemp.size()<<endl; //输出4 for(map<string,int>::iterator it=mapCount.begin();it!=mapCount.end();++it) { cout<<it->first<<" "<<it->second<<endl; }}