• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

(C++)错误的map删除操作和STL中容器的迭代器的底层实现机制

c# 搞代码 4年前 (2022-01-09) 28次浏览 已收录 0个评论

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;    }}

搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:(C++)错误的map删除操作和STL中容器的迭代器的底层实现机制

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址