在遍历中使用 iterator/reverse_iterator 进行 Erase 的用法

2023-11-21 17:51

本文主要是介绍在遍历中使用 iterator/reverse_iterator 进行 Erase 的用法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文转载自 罗朝晖的博客 http://blog.csdn.net/kesalin/article/details/24265303

众所周知,在使用迭代器遍历 STL 容器时,需要特别留意是否在循环中修改了迭代器而导致迭代器失效的情形。下面我来总结一下在对各种容器进行正向和反向遍历过程中删除元素时,正确更新迭代器的用法。本文源码:https://code.csdn.net/snippets/173595

首先,要明白使用正向迭代器(iterator)进行反向遍历是错误的用法,要不干嘛要有反向迭代器呢(reverse_iterator)。其次,根据容器的特性,遍历删除操作的用法可以分为两组,第一组是 list 和 vector,第二组是 map 和 set。


接下来,看看具体怎么个用法。

第一种情形:正向遍历删除元素

对 list 和 vector 来说,它们的 erase 函数会返回下一个迭代器,因此在遍历时,只需要 it = c.erase(it); 即可。

对 map 和 set 来说,它们的 erase 函数返回的 void,而在进行 erase 之后,当前迭代器会失效,无法再用于获取下一个迭代器。因此需要 erase 之前就获取指向下一个元素的迭代器。如: 

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. tmpIt = it;  
  2. ++it;  
  3. c.erase(tmpIt);  
利用后缀++操作符的特性(先创建副本,然后再递增迭代器,然后返回副本)上面的三行代码可以简化为一行:
[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. c.erase(it++);  


list 正向遍历删除元素示例(vector 用法相同):

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. list<int>::iterator it;  
  2. for (it = l.begin(); it != l.end();)  
  3. {  
  4.     if (0 == (*it) % 2) {  
  5.         it = l.erase(it);  
  6.     }  
  7.     else {  
  8.         ++it;  
  9.     }  
  10. }  

map 正向遍历删除元素示例(set 用法相同)

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. map<intint>::iterator mit;  
  2. for (mit = m.begin(); mit != m.end();)  
  3. {  
  4.     if (0 == mit->first % 2) {  
  5.         m.erase(mit++);  
  6.     }  
  7.     else {  
  8.         ++mit;  
  9.     }  
  10. }  

第二种情形,反向遍历删除元素

关于正向/反向迭代器的关系,请参考《Effective STL》,在这里我只说明一点,两者相差一个元素,从一个反向迭代器获得对应的正向迭代器需要使用 base() 方法。如下图所示:ri 是指向元素3的反向迭代器,而 i 是 ri.base() 所得到的正想迭代器。


由于所有的 erase 函数都只接受正向迭代器 iterator,所以在进行反向遍历删除元素时,首先需要将 reverse_iterator 转换为 iterator,然后再考虑更新迭代器的问题。

先来分析如何将 reverse_iterator 转换为 iterator。如上图所示,我们想要删除元素3,而 ri.base() 所得到的正向迭代器 i 指向的其实 4 了,因而为了正确地删除元素 3,需要将ri往前(反向的)挪一个位置。也就是说,这一步的删除用法应为:

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. c.erase((++rit).base());  

或:(想想为什么?,但这个用法不具备可移植性,因为有些 STL 实现不允许修改函数返回的指针)

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. c.erase(--(rit.base();  


然后,我们来分析迭代器更新的问题。
对 list/vector 来说,由于的 erase 能够返回一个有效的正向迭代器,因而只需要将返回的正向迭代器转换为反向迭代器即可。

对 map/set 来说,因为在进行删除操作 l.erase((++rit).base()) 时,迭代器已经更新过了,真是一举两得啊。从这里也可以看出,使用这种先递增后 base() 的转换删除法,代码更清晰。

至此,理论分析完毕,下面我们来看具体的实例。

list 反向遍历删除元素示例(vector 用法相同):

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. // erase with reverse_iterator  
  2. list<int>::reverse_iterator rit;  
  3. for (rit = l.rbegin(); rit != l.rend();)  
  4. {  
  5.     if (0 == (*rit) % 2) {  
  6.         rit = list<int>::reverse_iterator(l.erase((++rit).base()));  
  7.     }  
  8.     else {  
  9.         ++rit;  
  10.     }  
  11. }  

map 反向遍历删除元素示例(set 用法相同):

[cpp]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. // erase with reverse_iterator  
  2. map<intint>::reverse_iterator rit;  
  3. for (rit = m.rbegin(); rit != m.rend();)  
  4. {  
  5.     if (0 == rit->first % 2) {  
  6.         m.erase((++rit).base());  
  7.     }  
  8.     else {  
  9.         ++rit;  
  10.     }  
  11. }  

OK,删除用法相信大家都明白了,但是,但是,引起迭代器失效的操作还有插入操作呀,相信聪明的你一定能够举一反三正确更新迭代器~~

这篇关于在遍历中使用 iterator/reverse_iterator 进行 Erase 的用法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/404028

相关文章

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

使用Python删除Excel中的行列和单元格示例详解

《使用Python删除Excel中的行列和单元格示例详解》在处理Excel数据时,删除不需要的行、列或单元格是一项常见且必要的操作,本文将使用Python脚本实现对Excel表格的高效自动化处理,感兴... 目录开发环境准备使用 python 删除 Excphpel 表格中的行删除特定行删除空白行删除含指定

全面掌握 SQL 中的 DATEDIFF函数及用法最佳实践

《全面掌握SQL中的DATEDIFF函数及用法最佳实践》本文解析DATEDIFF在不同数据库中的差异,强调其边界计算原理,探讨应用场景及陷阱,推荐根据需求选择TIMESTAMPDIFF或inte... 目录1. 核心概念:DATEDIFF 究竟在计算什么?2. 主流数据库中的 DATEDIFF 实现2.1

SpringBoot结合Docker进行容器化处理指南

《SpringBoot结合Docker进行容器化处理指南》在当今快速发展的软件工程领域,SpringBoot和Docker已经成为现代Java开发者的必备工具,本文将深入讲解如何将一个SpringBo... 目录前言一、为什么选择 Spring Bootjavascript + docker1. 快速部署与

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

prometheus如何使用pushgateway监控网路丢包

《prometheus如何使用pushgateway监控网路丢包》:本文主要介绍prometheus如何使用pushgateway监控网路丢包问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录监控网路丢包脚本数据图表总结监控网路丢包脚本[root@gtcq-gt-monitor-prome

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

linux解压缩 xxx.jar文件进行内部操作过程

《linux解压缩xxx.jar文件进行内部操作过程》:本文主要介绍linux解压缩xxx.jar文件进行内部操作,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、解压文件二、压缩文件总结一、解压文件1、把 xxx.jar 文件放在服务器上,并进入当前目录#

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr