一、序列式容器(数组式容器)

对于序列式容器(如vector,deque),序列式容器就是数组式容器,删除当前的iterator会使后面所有元素的iterator都失效。这是因为vetor,deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置。所以不能使用erase(iter++)的方式,还好erase方法可以返回下一个有效的iterator。

1 for (iter = cont.begin(); iter != cont.end();)
2 {
3    (*it)->doSomething();
4    if (shouldDelete(*iter))
5       iter = cont.erase(iter);  //erase删除元素,返回下一个迭代器
6    else
7       ++iter;
8 }

迭代器失效:

 1 void vectorTest()
 2 {
 3     vector<int> container;
 4     for (int i = 0; i < 10; i++)
 5     {
 6         container.push_back(i);
 7     }
 8
 9     vector<int>::iterator iter;
10      for (iter = container.begin(); iter != container.end(); iter++)
11     {
12             if (*iter > 3)
13               container.erase(iter);
14     }
15
16      for (iter = container.begin(); iter != container.end(); iter++)
17     {
18             cout<<*iter<<endl;
19     }
20 }

报错是:vectoriterator not incrementable.

迭代器在执行++操作时报错!已经失效的迭代器不能再进行自增运算了。++代码大致实现如下:

1 _Myiter operator++(int)
2 {
3     _Myiter _Tmp=*this;
4     ++*this;
5     return (_Tmp);
6 }

对于序列式容器,比如vector,删除当前的iterator会使后面所有元素的iterator都失效。这是因为顺序容器内存是连续分配(分配一个数组作为内存),删除一个元素导致后面所有的元素会向前移动一个位置。(删除了一个元素,该元素后面的所有元素都要挪位置,所以,iter++,已经指向的是未知内存)。

但是erase方法可以返回下一个有效的iterator。所以代码做如下修改,就OK了。

 1 void vectorTest()
 2 {
 3     vector<int> container;
 4     for (int i = 0; i < 10; i++)
 5     {
 6         container.push_back(i);
 7     }
 8
 9     vector<int>::iterator iter;
10     for (iter = container.begin(); iter != container.end();)
11     {
12             if (*iter > 3) {
13                 iter = container.erase(iter);
14             }
15             else {
16                 iter ++;
17             }
18
19     }
20
21     for (iter = container.begin(); iter != container.end(); iter++)
22     {
23             cout<<*iter<<endl;
24     }
25 }

总结:vector是一个顺序容器,在内存中是一块连续的内存,当删除一个元素后,内存中的数据会发生移动,以保证数据的紧凑。所以删除一个数据后,其他数据的地址发生了变化,之前获取的迭代器根据原有的信息就访问不到正确的数据。

所以为了防止vector迭代器失效,常用如下方法:

1 for (iter = container.begin(); iter != container.end(); )
2 {
3             if (*iter > 3)
4               iter = container.erase(iter);    //erase的返回值是删除元素下一个元素的迭代器
5             else{
6                 iter++;
7             }
8 }

这样删除后iter指向的元素后,返回的是下一个元素的迭代器,这个迭代器是vector内存调整过后新的有效的迭代器。

二、关联式容器

对于关联容器(如map, set,multimap,multiset),删除当前的iterator,仅仅会使当前的iterator失效,只要在erase时,递增当前iterator即可。这是因为map之类的容器,使用了红黑树来实现,插入、删除一个结点不会对其他结点造成影响。erase迭代器只是被删元素的迭代器失效,但是返回值为void,所以要采用erase(iter++)的方式删除迭代器。

 1 for (iter = cont.begin(); it != cont.end();)
 2 {
 3    (*iter)->doSomething();
 4    if (shouldDelete(*iter))
 5       cont.erase(iter++);
 6    else
 7       ++iter;
 8 }
 9
10 //测试错误的Map删除元素
11 void mapTest()
12 {
13     map<int, string> dataMap;
14
15
16     for (int i = 0; i < 100; i++)
17     {
18            string strValue = "Hello, World";
19
20             stringstream ss;
21             ss<<i;
22             string tmpStrCount;
23             ss>>tmpStrCount;
24             strValue += tmpStrCount;
25             dataMap.insert(make_pair(i, strValue));
26     }
27
28     cout<<"MAP元素内容为:"<<endl;
29      map<int, string>::iterator iter;
30     for (iter = dataMap.begin(); iter != dataMap.end(); iter++)
31     {
32             int nKey = iter->first;
33             string strValue = iter->second;
34             cout<<strValue<<endl;
35     }
36
37     cout<<"内容开始删除:"<<endl;
38     //删除操作引发迭代器失效
39     for (iter = dataMap.begin(); iter != dataMap.end();iter++)
40     {
41             int nKey = iter->first;
42             string strValue = iter->second;
43
44            if (nKey % 2 == 0)
45            {
46                 dataMap.erase(iter);    //错误
47
48            }
49            /* cout<<iter->second<<endl;*/
50     }
51 }

出错:

解析:dataMap.erase(iter)之后,iter就已经失效了,所以iter无法自增,即iter++就会出bug.解决方案,就是在iter失效之前,先自增。

 1 void mapTest()
 2 {
 3     map<int, string> dataMap;
 4
 5
 6     for (int i = 0; i < 100; i++)
 7     {
 8            string strValue = "Hello, World";
 9
10             stringstream ss;
11             ss<<i;
12             string tmpStrCount;
13             ss>>tmpStrCount;
14             strValue += tmpStrCount;
15             dataMap.insert(make_pair(i, strValue));
16     }
17
18     cout<<"MAP元素内容为:"<<endl;
19     map<int, string>::iterator iter;
20     for (iter = dataMap.begin(); iter != dataMap.end(); iter++)
21     {
22             int nKey = iter->first;
23             string strValue = iter->second;
24             cout<<strValue<<endl;
25     }
26
27     cout<<"内容开始删除:"<<endl;
28     for (iter = dataMap.begin(); iter != dataMap.end();)
29     {
30             int nKey = iter->first;
31             string strValue = iter->second;
32
33            if (nKey % 2 == 0)
34            {
35                 dataMap.erase(iter++);
36                 auto a = iter;
37
38            }
39            else {
40                iter ++;
41            }
42     }
43 }

解析:dataMap.erase(iter++);这句话分三步走,先把iter传值到erase里面,然后iter自增,然后执行erase,所以iter在失效前已经自增了。

map是关联容器,以红黑树或者平衡二叉树组织数据,虽然删除了一个元素,整棵树也会调整,以符合红黑树或者二叉树的规范,但是单个节点在内存中的地址没有变化,变化的是各节点之间的指向关系。

所以在map中为了防止迭代器失效,在有删除操作时,常用如下方法:

 1 for (iter = dataMap.begin(); iter != dataMap.end(); )
 2 {
 3          int nKey = iter->first;
 4          string strValue = iter->second;
 5
 6          if (nKey % 2 == 0)
 7          {
 8                map<int, string>::iterator tmpIter = iter;
 9            iter++;
10                dataMap.erase(tmpIter);
11                //dataMap.erase(iter++) 这样也行
12
13          }else
14      {
15           iter++;
16          }
17 }

三、链表式容器

对于链表式容器(如list),删除当前的iterator,仅仅会使当前的iterator失效,这是因为list之类的容器,使用了链表来实现,插入、删除一个结点不会对其他结点造成影响。只要在erase时,递增当前iterator即可,并且erase方法可以返回下一个有效的iterator。

方式一:递增当前iterator

1 for (iter = cont.begin(); it != cont.end();)
2 {
3    (*iter)->doSomething();
4    if (shouldDelete(*iter))
5       cont.erase(iter++);
6    else
7       ++iter;
8 }

方式二:通过erase获得下一个有效的iterator

1 for (iter = cont.begin(); iter != cont.end();)
2 {
3    (*it)->doSomething();
4    if (shouldDelete(*iter))
5       iter = cont.erase(iter);  //erase删除元素,返回下一个迭代器
6    else
7       ++iter;
8 }

四、总结

迭代器失效分三种情况考虑,也是分三种数据结构考虑,分别为数组型,链表型,树型数据结构。

数组型数据结构:该数据结构的元素是分配在连续的内存中,insert和erase操作,都会使得删除点和插入点之后的元素挪位置,所以,插入点和删除掉之后的迭代器全部失效,也就是说insert(*iter)(或erase(*iter)),然后在iter++,是没有意义的。解决方法:erase(*iter)的返回值是下一个有效迭代器的值。 iter =cont.erase(iter);

链表型数据结构:对于list型的数据结构,使用了不连续分配的内存,删除运算使指向删除位置的迭代器失效,但是不会失效其他迭代器.解决办法两种,erase(*iter)会返回下一个有效迭代器的值,或者erase(iter++).

树形数据结构: 使用红黑树来存储数据,插入不会使得任何迭代器失效;删除运算使指向删除位置的迭代器失效,但是不会失效其他迭代器.erase迭代器只是被删元素的迭代器失效,但是返回值为void,所以要采用erase(iter++)的方式删除迭代器。

注意:经过erase(iter)之后的迭代器完全失效,该迭代器iter不能参与任何运算,包括iter++,*ite

C++迭代器失效的几种情况总结相关推荐

  1. java 迭代器失效_迭代器失效的几种情况

    关于迭代器失效,,今天做一个总结. 迭代器失效分三种情况考虑,也是三种数据结构考虑,分别为数组型,链表型,树型数据结构. 1.对于序列式容器,比如vector,删除当前的iterator会使后面所有元 ...

  2. spring中事务失效的几种情况

    下面简单介绍下,spring中常见的事务失效的几种情况.让我们在开发的过程避免这些情况,写出正确而且优雅的代码. 文章目录 数据库引擎不支持,mysql需要InnoDB 方法必须是public的 方法 ...

  3. mysql数据索引失效_MySQL索引失效的几种情况

    1.索引无法存储null值 a.单列索引无法储null值,复合索引无法储全为null的值. b.查询时,采用is null条件时,不能利用到索引,只能全表扫描. 为什么索引列无法存储Null值? a. ...

  4. [索引] 索引失效的几种情况

    一.单表索引失效的几种情况 建立员工记录表 CREATE TABLE `staffs` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255 ...

  5. MySQL索引失效的9种情况(针对InnoDB存储引擎)

    前言 MySQL中提高查询性能的最有效的方式之一就是对数据表合理的设计索引,优秀的索引的设计方案很大程度上可以提高查询的性能. 因此,索引对查询的速度有着至关重要的影响. 为了尽量的使优化器用到我们的 ...

  6. sql索引失效的几种情况

    sql索引失效的几种情况 1.使用 != 或者 <> 导致索引失效 2.类型不一致导致索引失效 3.函数导致索引失效 4.运算符导致索引失效 5.模糊搜索导致索引失效 6.NOT IN.N ...

  7. MyBatis一级缓存失效的几种情况

    MyBatis一级缓存失效的几种情况 文章目录 MyBatis一级缓存失效的几种情况 1 MyBatis一级缓存概述 2 四种失效的基本情况 3 几种特殊情况 1 MyBatis一级缓存概述 MyBa ...

  8. Mysql索引会失效的几种情况分析

    转自:http://www.jb51.net/article/50649.htm 在做项目的过程中,难免会遇到明明给mysql建立了索引,可是查询还是很缓慢的情况出现,下面我们来具体分析下这种情况出现 ...

  9. oracle or索引失效_oracle数据库中索引会失效的几种情况

    创建Oracle 索引的目的是为了避免全表扫描数据,提高查询效率,但是如果sql语句写的不好致使索引失效,反而会影响数据查询效率.以下几种情况就会导致索引失效: 没有 WHERE 子句 众所周知,添加 ...

最新文章

  1. arduino nano 蓝牙_探索 Golang 云原生游戏服务器开发,5 分钟上手 Nano 游戏服务器框架...
  2. mysql''和null,mysql中NULL和null的区别
  3. xcode新版本single view_动态数组函数系列1|概况-跟以往Excel版本完全不一样玩法的函数...
  4. OpenCV成长之路:图像滤波
  5. 混编ObjectiveC++
  6. EhCache 常用配置项详解
  7. linux网络子系统研究:数据收发简略流程图
  8. android 筛选菜单_使用C语言开发跨平台(win/android)应用(PainterEngine 快速入门教程)...
  9. POJ 1952 DP
  10. 记录.NET Core部署到Linux之.NET Core环境搭建(1)
  11. 【iOS 15】iPhone如何录屏?iPhone屏幕录制技巧分享
  12. SQL账户SA登录失败,提示错误:18456
  13. 基于SpringBoot的QQ邮箱登录注册
  14. 让样本不一样重要-A Dual Weighting Label Assignment Scheme for Object Detection
  15. c++11新特性std::is_trivial
  16. Linux系统ln -s命令,详解Linux ln 命令
  17. 蓝桥杯——机器人行走(模拟类题目)Java语言实现
  18. 自动化脚本Cron工具(MAC和Linux系统)
  19. 北航计算机组成原理课程设计-2021秋 PreProject-MIPS-测试程序设计
  20. 写给2018年底的我的一封信

热门文章

  1. boost::function_types::is_function_pointer用法的测试程序
  2. boost::exception的用法测试
  3. Boost:bimap双图的range范围的测试程序
  4. Boost:can_prefer的使用测试程序
  5. ITK:创建一个大小Size
  6. DCMTK:DcmElement :: calcElementLength的测试程序
  7. VTK:图片之ImageFFT
  8. OpenCV相位校正phase corr的实例(附完整代码)
  9. Qt Creator连接Android设备
  10. c++fibonacci search斐波那契搜索的实现算法(附完整源码)