C++基本语言:1.9迭代器精彩演绎,失效分析及弥补、实战

C++基本语言包含10章节内容,存于C++从入门到精通专栏

目录

一、迭代器简介

二、容器的迭代器类型

三、迭代器begin()/end()、反向迭代器rbegin/rend操作

1.迭代器

1.1begin和end

1.2 反向迭代器 rbegin()和rend()

四、迭代器运算符

五、迭代器运算符 const_iterator迭代器(只读)

cbegin和cend

六、迭代器失效

6.1添加元素和从容器中删除元素操作要小心

6.2不同的容器实现机理不同

6.3灾难程序演示

灾难程序演示1

灾难程序演示2

七、范例演示

1)用迭代器遍历string类型数据

2)vector容器常用操作与内存释放

一、迭代器简介

vector是一个容器,里面可以容纳很多对象。

迭代器是一种遍历容器内元素的 数据类型。

这种数据类型感觉有点像指针,读者就理解为迭代器是用来指向容器中的 某个元素 的。

可以通过“[]”(下标)访问string字符串中的字符、访问vector中的元素。

但实际上,在C++中,很少通过下标来访问它们,一般都是采用迭代器来访问(更通用)

因为除了vector容器外,C++标准库中还有几个其他种类的容器。这些容器都可以使用迭代器来遍历其中的元素内容

string其实是字符串,不属于容器,但string也支持用迭代器遍历。

通过迭代器,可以读取容器中的元素值、修改容器中某个迭代器所指向的元素值。

此外,迭代器可以像指针一样,通过++、--等 运算符从容器中的一个元素移动到另一个元素。

在C++标准库中,还有其他容器如list、 map等都属于比较常用的容器,C++标准库为每个这些容器都定义了对应的一种迭代器类型,有很多容器不支持“[]”操作,但容器都支持迭代器操作。

结论:尽量用迭代器来访问容器中的元素。  

二、容器的迭代器类型

C++标准库为每种容器都定义了对应的迭代器类型。这里就以容器vector为例

7816d02911d34b96b10996aa826764e2.png这个变量的类型是vector<int>::iterator类型

“::iterator”是每个容器(如vector)里面都定义了的一个成员(类型名),这个名字是固定的,请牢记。

在理解的时候,就把整个vector<int>::iterator理解成一种类型, 这种类型就专门应用于迭代器,当用这个类型定义一个变量的时候iter,这个变量就是一个迭代器。

三、迭代器begin()/end()、反向迭代器rbegin/rend操作

1.迭代器

每一种容器,如vector,都定义了begin和end的成员函数

1.1begin和end

这两个成员函数正好用来返回迭代器类型。

(1)begin返回一个迭代器类型(就理解成返回一个迭代器)

8b53c91648894506bfaf8a3090c8cc59.png

791efa543be343039c03c6e1f8650bb4.png

(2)end返回一个迭代器类型(就理解成返回一个迭代器)

f614e14631604e63a8a01c3d39fc8b1a.png

对上面的代码进行跟踪调试,观察begin和end结果可以看到, end()指向了一个乱数字

e29f190ee5d14c4d91020941cb906c6f.png

(3)如果容器为空,则begin返回的迭代器和end返回的迭代器相同。

1e88e5e31aac4df0a4f4157252a44569.png

结论:end返回的迭代器并不指向容器vector中的任何元素,它起到实际上是一个标志(岗哨)作用,如果迭代器从容器的begin位置开始不断往后游走,也就是不断遍历容器中的元素,那么如果有一个时刻,iter 走到了end位置,那就表示已经遍历完了容器中的所有元素。

(4)写一段代码,传统的通过迭代器访问容器中元素的方法(100,200,300)

99ef65a842d74f8c803cde1a3b114f41.png

1.2 反向迭代器 rbegin()和rend()

想从后面往前遍历一个容器,那么反向迭代器就比较方便。

反向迭代器使用的是rbegin成员函数和rend成员函数。

 1)rbegin返回一个反向迭代器类型,指向容器的最后一个元素。

 2)rend返回一个反向迭代器类型,指向容器的第一个元素的前面位置。

025ce6d6a32c4788ad6e227f89b93fc3.png

范例:运行起来看结果:300、200、100

eefc6948ce174e0da7b54f9eebb05c71.png

四、迭代器运算符

(1)*iter:返回迭代器iter所指向元素的引用。(类似于*p)

必须要保证该迭代器指向的是有效的容器元素,不能指向end,因为end是末端元素后面的位置 ,也就是说,end 已 经指向了一个不存在元素 。

(2)++iter/iter++:让迭代器指向容器中的下一个元素。

但是已经指向end的迭代器,不能再++,否则运行时报错。

(3)--iter 和 iter--: 让迭代器指向容器中的前一个元素。

但是已经指向begin的迭代器,不能再--,否则运行时报错。

38cf1b41d1904a848b2b15e58312cf05.png

5638d00474fc4addb9717a5d8da18cd4.png

(4)iter1==iter2或iter1!=iter2:判断两个迭代器是否相等。如果两个迭代器指向的是同一个元素,就相等,否则就不相等。

(5)如何引用结构中的成员

5b299b1d2c93467bb86c5edadf1316c3.png

请注意:一定要确保迭代器指向有效的容器中的元素,否则范例中的 这些行为可能会导致意想不到的结果。

还有很多其他运算符,例如迭代器之间可以相减表示两个迭代器之间 的距离,迭代器加一个数字表示跳过多少个元素,不过这些都不常用,不准备逐一介绍,意义也不大。

五、迭代器运算符 const_iterator迭代器(只读)

实际上每种容器还有另外一种迭代器类型,叫作const_iterator

含义:有const 在,一般都表示常量,也就是说值不能改变的意思。

这里的值不能改变表示该迭代器指向的元素的值不能改变,并不表示该迭代器本身不能改变。也就是说,该迭代器是可以不断地指向容器中的下一个元素的。

terator只能从容器中读元素,不能通过该迭代器修改容器中的元素。

所以说,const_iterator更像一个常量指针,而 iterator迭代器是能读能写的。

357f654458944c058147ce3fcb36692a.png

什么时候用const_iterator呢?

如果这个容器对象是一个常量,那么就必须使用const_iterator,否则报错:

25429fd6b96248c59d4964a2e6fc5943.png

cbegin和cend

C++11引入的两个 新函数cbegin和cend成员函数,与begin、end非常类似。

不管容器是否是常量容器,cbegin、cend返回的都是常量迭代器const_iterator。

f58e6a3634b34d29ab8b9df066afda00.png

六、迭代器失效

6.1添加元素和从容器中删除元素操作要小心

如果在for循环中,通过push_back等手段往容器中增加元素,范围 for循环输出的容器中元素就会混乱,要么有结果但混乱,要么直接崩溃。

其实,范围for语句等价于常规的用迭代器对容器进行操作。

72bbac29a5bd4a8da8dd5951b46a4ea3.png

等价于迭代器这种操作方式:

ec5f147da52f47618832b5bbb870057e.png

一旦在for循环中增删容器中的元素,就会导致迭代器失效, 整个结果就混乱了。

     其实,任何一种能够改变vector对象容量的操作,如push_back, 都会使当前的vector对象迭代器失效,请读者谨记:在操作迭代器的过程中(使用了迭代器的这种循环体)

千万不要改变vector对象的容量,也就是不要增加或者删除vector容器中的元素。

94131d18bdd74b488f6e724a27f5505a.png

对于向容器中添加元素和从容器中删除元素操作要小心,因为这些操作可能都会使指向容器元素的迭代器(也包括指针、引用等)失效。

失效就表示它不能再代表任何容器中的元素,一旦使用这种失效的迭代器,就表示程序的书写犯了严重错误,很多情况下都会导致程序崩溃,就好比使用了没有被初始化的指针一样。

6.2不同的容器实现机理不同

(例如有的容器内部数据是连续存储的,插入元素时一旦原有内存不够用,则可能就会导致容器中原有数据全部迁移到一个新内存去,如vector等容器),不同的插入操作、不同的插入位 置,会导致迭代器、指针、引用部分或者全部失效。

甚至在循环体中的诸如vecvalue.end()代码都会因为插入数据操作导致失效。

另一种情况是删除操作。如果从容器中删除一个元素,那么,当前指向这个被删除元素的迭代器、指针、引用肯定是立即失效,绝不能再引用它们。 此外,不同的容器,针对删除操作,不同的删除位置,也会导致迭代器 、 指 针 、 引用部分或者全部失效 , 甚 至 在 循 环 体 中 的 诸 如 vecvalue.end()代码都会因为删除数据操作导致失效。

解决方法就是:

如果在一个使用了迭代器的循环中插入元素到容器, 那只插入一个元素后就应该立刻跳出循环体,不能再继续用这些迭代器操作容器。

8a4845f4b9ed4467acd463b2c9793b7c.png

效果:

76a4c486ceff4defb7ddbf3be51c9596.png

6.3灾难程序演示

灾难程序演示1

下面代码目前一切没有问题:

180de23f225e459a9180515e74aa5697.png

接着,往循环中增加代码,注意while循环体中代码的变化:

d04694791c7c449aa52b973c1915ed10.png

如果有如下需求:

想不断地插入多条数据,并且还希望迭代器不失效,那就得查资料研究,如研究针对vector容器,如何写 insert这段代码,才能让迭代器不失效,让程序安全地运行。看如下代 码,是一种满足连续插入多条数据的解决方案:

相当于每次更新end,防止end失效

insert后返回的还是迭代器,指向(icount+80)

7df68d13852f4b7395079e00769135f8.png

*beg的值依次是:80,81……90

太细节的东西就不过多涉及,迭代器会失效的道理读者都懂了。

一般的做法就是如果这个循环和迭代器有关,基本都只会做插入或删除操作一次,然后会立即break,因为保不准插入或删除操作导致哪个迭代器失效,所以立即break到循环体到外面去。

灾难程序演示2

在一个程序运行结束之前,可能会习惯性地释放掉vector里面的内容。

有些人可能会写出这样的代码来在程序的最后进行释放处理:

789d0f0d28864e4787bb9c623ab14782.png

运行后程序崩溃。肯定是迭代器失效导致崩溃。那就一定得要小心这种释放代码。

经过分析,erase会返回下一个元素位置,这个位置肯定要想办法保存,但因为这里用的是for循环,for循环里每次还有++iter这种操作,所 以怎样改造能够让它安全释放,这是一个问题。经过思考和研究,找到了 一种写法能够让容器顺利释放:

8d66991d7d574df283447500d7288031.png

所以,如果说要把容器一下全部清空,用clear还是其他方法也好, 都还算简单。

可以使用 iv.clear();

但是如果需要用和迭代相关的循环来一个元素一个元素地删除,那一定更要注意。

这里笔者推荐一个简单直接且有效的方法:

d7f761d099804cf4be7f7c0c65dccbee.png

七、范例演示

1)用迭代器遍历string类型数据

563d636ceb4945248ddb8c083b26626c.png

2)vector容器常用操作与内存释放

实践程序:

假设有一些配置项,配置项里记录一些配置数据,当然这些配置项正常来讲应该写在文件中,这样方便随时修改,但因为是演示目的,就写在代码中即可。

a951db8ffc3e48d9a559068386896fdc.png

然后是主函数

e141368a43b14c7888a9b2fe248bc3b4.png

6222898c32104b25a2b021b9b3b99320.png

这里用到了一个函数

41b0ab123685466eba9dee3b27dca080.png

看一看conflist容器结构示意图

4729330ccec0431189bcae173824111c.png

902fe782d6f8494eb41b37101c784d16.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/287911.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++ DAY5 作业

1.全局变量,int monster 10000;定义英雄类hero,受保护的属性string name,int hp.int attck;公有的无参构造,有参构造,虚成员函数void Atk()blood-0;},法师类继承自英雄类,私有属性int ap_atk50…

MatrixOne 1.1.0 Release

我们非常高兴地宣布: MatrixOne内核1.1.0版本 正式发布啦! 项目文档网站 https://docs.matrixorigin.cn MatrixOne是一款分布式超融合异构数据库,MatrixOne旨在提供一个云原生、高性能、高弹性、高度兼容MySQL的HSTAP数据库,让…

SpringCloud微服务架构,适合接私(附源码)

一个由商业级项目升级优化而来的微服务架构,采用SpringBoot 2.7 、SpringCloud 等核心技术构建,提供基于React和Vue的两个前端框架用于快速搭建企业级的SaaS多租户微服务平台。 架构图 项目介绍 用户权益 仅允许免费用于学习、毕设、公司项目、私活等。…

Java技术栈 —— Hadoop入门(一)

Java技术栈 —— Hadoop入门(一) 一、Hadoop第一印象二、安装Hadoop三、Hadoop解析3.1 Hadoop生态介绍3.1.1 MapReduce - 核心组件3.1.2 HDFS - 核心组件3.1.3 YARN - 核心组件3.1.4 其它组件3.1.4.1 HBase3.1.4.2 Hive3.1.4.3 Spark 一、Hadoop第一印象…

IoTDB 集群部署——windows

本文的测试环境为window server2016,版本包为1.1.0,jdk版本为1.8 首先下载IoTDB版本包,链接地址如下 https://archive.apache.org/dist/iotdb/1.1.0/apache-iotdb-1.1.0-all-bin.zip 本次部署将使用1个ConfigNode 和3个DataNode模式&#…

Java基础-----集合类(一)

文章目录 1.集合类简介2. 自定义集合类 1.集合类简介 集合和数组一样,都是用来存储多个数据的结构,也可以称作容器。 数组长度是不可变化的,一旦在初始化数组时指定了数组长度,这个长度就不可变。如果需要处理数量变化的数据&am…

我在CSDN的2023年

一、引言 在2023年的这一年当中,在CSDN的生活让我得到许多知识与启发,也让我获得一些快乐和成就 二、自己的收获 在这一年当中,我从一个只会看别人写的文章解决问题到,可以自己写文章帮别人解决问题,这种成就感是极大…

【数据不完整?用EM算法填补缺失】期望值最大化 EM 算法:睹始知终

期望值最大化算法 EM:睹始知终 算法思想算法推导算法流程E步骤:期望M步骤:最大化陷入局部最优的原因 算法应用高斯混合模型(Gaussian Mixture Model, GMM)问题描述输入输出Python代码实现 算法思想 期望值最大化方法&a…

手把手教你学会接口自动化框架的搭建-前言

在网上看过很多帖子,各种接口自动化的框架眼花缭乱,但是对于很多才做自动化的新手,那是一头雾水。 因此,我决定出一个系列,让你能够按照我的文档一步步把接口自动化都做起来,而不是网上这种一股脑的全部抛出,让你看的云里雾里的。 看完这些文档保证你能去任何一家公司,…

面对众多知识付费平台,如何做出明智的选择?

在当今的知识付费市场中,用户面临的选择越来越多,如何从众多知识付费平台中正确选择属于自己的平台呢?下面,我们将为您介绍明理信息科技知识付费平台相比同行的优势,帮助您做出明智的选择。 一、创新的技术架构&#…

Python Web框架FastAPI——一个比Flask和Tornada更高性能的API框架

目录 一、FastAPI框架概述 二、FastAPI与Flask和Tornado的性能对比 1、路由性能 2、请求处理性能 3、内存占用 三、FastAPI的优点与特色 四、代码示例 五、注意事项 六、结论 在当今的软件开发领域,快速、高效地构建API成为了许多项目的关键需求。为了满足…

DevEco Studio IP Convention for MAC

一、前置条件 1、已经Phone/Tablet和PC连接到同一WLAN网络。 2、已经获取Phone/Tablet的IP地址,可通过设置>关于手机/关于平板>状态信息>IP地址进行 查看 3、Phone/Tablet上的555…

Nginx多域名部署多站点

目录 1.修改配置文件nginx.conf 2. 修改hosts文件 1.修改配置文件nginx.conf 在配置文件的 server_name 处修改成自己需要的域名,然后保存退出 j 查看语法是否错误,然后重启nginx nginx -t # 查看语法是否正确 systemctl restart nginx # 重启nginx …

【Python机器学习】观察数据散点图矩阵

构建机器学习模型前,通常要检查数据,判断不用机器学习能不能轻松完成任务,或者需要的信息有没有包含在数据中。检查数据也是发现异常值和特殊值的好办法。 检查数据的最佳方法之一就是可视化,一种是绘制散点图,将一个…

CNN——LeNet

1.LeNet概述 LeNet是Yann LeCun于1988年提出的用于手写体数字识别的网络结构,它是最早发布的卷积神经网络之一,可以说LeNet是深度CNN网络的基石。 当时,LeNet取得了与支持向量机(support vector machines)性能相…

【前沿技术】超级稳定的视频卡通画方案

Git clone项目到本地 git clone gitgithub.com:Artiprocher/DiffSynth-Studio.git 基本原理 使用了stable diffusion稳定扩散模型和controlnet来控制图像生成的轮廓,animatediff控制视频帧与帧之间的连续性,最后使用RIFE技术平滑整个生成后的视频。 …

40道java集合面试题含答案(很全)

1. 什么是集合 集合就是一个放数据的容器,准确的说是放数据对象引用的容器集合类存放的都是对象的引用,而不是对象的本身集合类型主要有3种:set(集)、list(列表)和map(映射)。 2. 集合的特点 集合的特点主要有如下两…

使用Python做个可视化的“剪刀石头布”小游戏

目录 一、引言 二、环境准备与基础知识 三、游戏界面制作 四、游戏逻辑实现 五、代码示例 六、游戏测试与优化 七、扩展与改进 八、总结 一、引言 “剪刀石头布”是一种古老的手势游戏,它简单易懂,趣味性强,适合各个年龄段的人参与。…

虎克:开发小程序要多少钱一个,非专业开发如何做自己的小程序

小程序开发费用主要取决于小程序的功能复杂度和开发周期。一般来说,小程序开发费用可以分为两类:模板开发和定制开发。 模板开发:模板开发是指使用现成的模板进行开发,价格相对较低,一般在几千元左右。优点是价格便宜&…

你不知道的 CSS 之 包含块 ! 最细讲解,一听就懂!

你不知道的 CSS 之包含块 一说到 CSS 盒模型,这是很多小伙伴耳熟能详的知识,甚至有的小伙伴还能说出 border-box 和 content-box 这两种盒模型的区别。 但是一说到 CSS 包含块,有的小伙伴就懵圈了,什么是包含块?好像…