my_container.erase(iter);  

  其中my_container是STL的某种容器,iter是指向这个容器中某个元素的迭代器。如果不是在for,while循环中,这种方式删除元素没有问题,如果是在for,while中对m_container迭代,删除其中符合条件的所有元素,就可能出现问题。如果是在for,while中对m_container迭代,删除其中符合条件的所有元素,就可能出现问题。

  问题是:

  在迭代容器的时候删除元素,可能导致迭代器失效(invalidation of iterators),产生未定义行为(undefined behavior);例如,对某个迭代器解引用所获得的值并不是执行erase()前这个迭代器指向的值,还有可能对未指向任何元素的迭代器的解引用赋值而引发程序crash。

  类似的问题代码像这样:

.std::vector<int>  my_container;
.for (int i = ; i < ; ++i) {
. my_container.push_back(i);
.bsp; }
.
.std::vector<int>::iterator it = my_container.begin();
.for (it != my_container.end(); it++) {
. if (*it % == ) {
. my_container.erase(it);
. }
.}

  my_container.erase(it)之后,it及其后面的迭代器已经失效,不应该再使用这些迭代器。再执行it++,其行为是未定义的。

其他容器也会遇到迭代器失效的问题:

对于vector 被删除元素的迭代器以及指向后面元素的迭代器全部失效。  

对于deque  在首部或尾部删除元素则只会使指向被删除元素的迭代器失效,任何其他位置的插入和删除操作将使指向该容器元素的 所有迭代器失效。  

对于list 仅有指向被删除元素的迭代器失效。    

对于(mulit)map ,(multi)set 仅有指向被删除元素的迭代器失效。

所以Golden Rule是:尽量不要使用容器的插入删除操作之前的迭代器。

为什么不同容器迭代器失效情况有差别?这与实现各容器的数据结构有关。  

如何在迭代容器时删除其中的元素?各容器通用的做法如下:

std::vector<int>::iterator it = my_container.begin();
. for (it != my_container.end();/**blank*/ ) {
. if (*it % == ) {
. my_container.erase(it++);
. }
. else{
. it++;
. }
. }

my_container.erase(it++) 巧妙得在执行erase()之前,it 先自增,指向被删除元素后面的元素,而给erase()传递的是未自增的it迭代器,

以定位要删除的元素。如果元素的值为奇数,则删除此元素,it指向下一个元素,如果元素的值为偶数,则检查下一个元素的值。整个迭代过程中

迭代器就不会失效了。

上段代码中两个不同分支出现了i++操作,下面的代码示例显示了如何防止遗忘其中任何一个分支的i++操作。

MyContainer::iterator it = myContainer.begin();
. While(it != myContainer.end()){
.   MyContainer::iterator curIt = it;
.   if (*curIt == matchingValue) {
.   myContainer.erase(curIt);
.   }
. }

对于vector ,deque, list, 另一种可行的方式是:

.std::vector<int>::iterator it = my_container.begin();
.for (it != my_container.end();/**blank*/ ) {
. if (*it % == ) {
. it = my_container.erase(it);
. }
. else{
. it++;
. }
.}

上面代码可行的原因是vector::erase() 返回一个新的迭代器,指向被删除元素的后面的元素。可以继续使用新的迭代器。

而出于某种未知的原因(multi)map::erase(), (multi)set::erase()没有返回这样的迭代器。(从C++11开始也支持返回迭代器了).

但是对于vector,诸如在0到99个数中删除所有奇数的问题,可以使用STL的remove(),remove_if()优化性能。代码如下:

.bool isOdd(int value)
.{
. return (value % ) == ;
.}
.
.my_container.erase( std::remove_if(m_container.begin(), m_container.end(), isOdd), m_container.end());

让我们再看看不使用remove_if()的版本:

.for (it != my_container.end();/**blank*/ ) {
. if (*it % == ) {
. it = my_container.erase(it);
. }
. else{
. it++;
. }
. }

如果你阅读过erase()源码或了解它是如何工作的,性能问题就显而易见:erase()删除一个元素的操作是被删除元素后面的所有元素依次

向前移动一个元素的位置,然后删除最后一个元素,时间复杂度为O(n^2)。

remove(),remove_if()的时间复杂度为O(n),删除元素的操作如下所示:

.template<class ForwardIt, class UnaryPredicate>
.ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p)
.{
. ForwardIt result = first;
. for (; first != last; ++first) {
. if (!p(*first)) {
. *result++ = *first;
. }
. }
. return result;
. }

从前向后遍历容器所有元素,将待保留的元素向前移动,占据待删除的元素的位置,remove_if()返回新的元素范围(begin,end)中的end,

记为new_end_of_range ,再调用erase()删除从new_end_of_range到my_container.end()之间的所有元素。

实际上,remove_if() 没有删除容器中的任何元素,它没有改变my_container.end(), 调用remove_if()后容器元素个数不会改变!!删除元素的工作

交给了erase().

                    

Scott Meyers在他的”Effective STL”中关于此问题的讨论中也使用了remove_if(),由此看来,他的确是提出了一些让STL effective的建议。

深入学习STL迭代器失效问题:

在google中搜索 stl iterator invalidation rules 可以获得很多有关STL迭代器失效的有关内容。

References:

1. STL remove_if()       http://en.cppreference.com/w/cpp/algorithm/remove

2.More C++ Idioms/Erase-Remove   http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Erase-Remove

3.Effective STL, Item 32 - Scott Meyers

4.Cpp Invalid Iterators [对各种迭代器失效的情况进行了讲解分类]

http://www.angelikalanger.com/Conferences/Slides/CppInvalidIterators-DevConnections-2002.pdf

5.以下是stackoverflow上关于在迭代时删除容器中元素的讨论:

http://stackoverflow.com/questions/1604588/iterate-vector-remove-certain-items-as-i-go

http://stackoverflow.com/questions/3747691/stdvector-iterator-invalidation?rq=1

http://stackoverflow.com/questions/2874441/deleting-elements-from-stl-set-while-iterating?rq=1

http://stackoverflow.com/questions/1038708/erase-remove-contents-from-the-map-or-any-other-stl

-container-while-iterating/1038761#1038761

http://stackoverflow.com/questions/799314/difference-between-erase-and-remove?rq=1

-------------------------------------------------------------------------------------------------------------------------------------------------------------

http://hi.baidu.com/ewook/item/53e11ec2a5bea42c47d5c065

http://blog.chinaunix.net/uid-26335251-id-3347483.html

http://www.cnblogs.com/yc_sunniwell/archive/2010/06/25/1764934.html

C++ STL中迭代器失效的问题的更多相关文章

  1. C++中防止STL中迭代器失效——map/set等关联容器——vector/list/deque等序列容器—如何防止迭代器失效—即erase()的使用

    序列性容器::(vector和list和deque)   erase迭代器不仅使所有指向被删元素的迭代器失效,而且使被   删元素之后的所有迭代器失效,所以不能使用erase(iter++)的方 式, ...

  2. STL容器迭代器失效问题讨论

    STL源码剖析---迭代器失效小结 vector迭代器的几种失效的情况: .当插入(push_back)一个元素后,end操作返回的迭代器肯定失效. .当插入(push_back)一个元素后,capa ...

  3. STL容器迭代器失效分析

    连续内存序列容器(vector, string, deque) 对于连续内存序列STL容器,例如vector,string,deque,删除当前iterator会使得后面所有的iterator都失效, ...

  4. stl vector、红黑树、set、multiset、map、multimap、迭代器失效、哈希表(hash_table)、hashset、hashmap、unordered_map、list

    stl:即标准模板库,该库包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法 六大组件: 容器.迭代器.算法.仿函数.空间配置器.迭代适配器 迭代器:迭代器(iterator)是一种抽象的设计 ...

  5. STL中的迭代器分类

      STL中迭代器的分类 五类迭代器如下: 1.输入迭代器:只读,一次传递    为输入迭代器预定义实现只有istream_iterator和istreambuf_iterator,用于从一个输入流i ...

  6. STL中实现 iterator trail 的编程技巧

    STL中实现 iterator trail 的编程技巧 <泛型编程和 STL>笔记及思考. 这篇文章主要记录在 STL 中迭代器设计过程中出现的编程技巧,围绕的 STL 主题为 (迭代器特 ...

  7. C++ 标准模板库(STL)——迭代器(iterators)的用法及理解

    C++ STL中迭代器(iterators)用于遍历对象集合的元素.由于容器大小随着插入删除等操作动态改变,无法像静态数组那样获取数组长度然后遍历容器里的所有元素:这时就需要迭代器,每次从容器内第一个 ...

  8. C++ STL 迭代器失效问题

    之前看<C++ Primier>的时候,也解到在顺序型窗口里insert/erase会涉及到迭代器失效的问题,并没有深究.今天写程序的时候遇到了这个问题. 1 莫名其妙的Erase 最初我 ...

  9. STL的erase()陷阱-迭代器失效总结

    下面材料整理自Internet&著作. STL中的容器按存储方式分为两类,一类是按以数组形式存储的容器(如:vector .deque):另一类是以不连续的节点形式存储的容器(如:list.s ...

随机推荐

  1. TCP/IP协议原理与应用笔记19:IP分组的交付和路由选择

    1. 引言: (1)互联网结构: 信息:IP分组(直接广播地址(Directed Broadcast Address),其指定了在一个特定网络中的"所有主机".) 节点:路由器.主 ...

  2. GIT免密码PUSH

    摘自:dudu 备忘,感谢! 1. 在Windows中添加一个HOME环境变量,值为%USERPROFILE%,如下图: 2. 在“开始>运行”中打开%Home%,新建一个名为“_netrc”的 ...

  3. 【转】所需即所获:像 IDE 一样使用 vim

    转自:  https://github.com/yangyangwithgnu/use_vim_as_ide 所需即所获:像 IDE 一样使用 vim yangyangwithgnu@yeah.net ...

  4. AppScan安全问题解决方案

    一. 环境准备 测试通常给的是PDF文档,动辄几百页,看起来很费劲,看文档的时间可能比解决问题的时间还长...所以作为需要解决问题的我们来说,最好安装AppScan,请测试人员提供类型为AppScan ...

  5. (转)如何把exe的flash还原成swf

    首先要准备一个16进制字节码编辑器,这里用的是UltraEdit,可以到这里下载它最新的破解版.现在我们开始我们的还原工作吧.1.用UltraEdit打开待还原的exe文件.你就会看到这个exe的字节 ...

  6. 第二十九篇、CoreAnimation的使用

    使用的的三个步骤 1.初始化演员 2.设置好剧情 3.播放 主要类: CALayer // 绘图部分 CABaseAnimation // 基本动画(缩放,移动) CAKeyframeAnimatio ...

  7. HTML+CSS学习笔记(4) - 认识标签(3)

    HTML+CSS学习笔记(4) - 认识标签(3) 1.使用<a>标签,链接到另一个页面 使用<a>标签可实现超链接,它在网页制作中可以说是无处不在,只要有链接的地方,就会有这 ...

  8. HTML+CSS学习笔记(2) - 认识标签(1)

    HTML+CSS学习笔记(2) - 认识标签(1) 1.语义化,让你的网页更好的被搜索引擎理解 标签的用途: 我们学习网页制作时,常常会听到一个词,语义化.那么什么叫做语义化呢,说的通俗点就是:明白每 ...

  9. ASP.NET实现折线图的绘制

    用到.Net中绘图类,实现折线图的绘制,生成图片,在页面的显示,代码如下: /// <summary> /// 获取数据 /// strChartName:图名称: /// yName:纵 ...

  10. (转)Ehcache作为分布式缓存的研究

    ehcache支持两种拓扑结构,一种是Distributed Caching,另一种是Replicated Caching Distributed Caching 这和一般意义上的分布式缓存非常类似, ...