掘根宝典之C++迭代器简介
简介
迭代器是一种用于遍历容器元素的对象。它提供了一种统一的访问方式,使程序员可以对容器中的元素进行逐个访问和操作,而不需要了解容器的内部实现细节。
C++标准库里每个容器都定义了迭代器
迭代器的作用类似于指针,可以指向容器中的某个元素,并通过操作迭代器来访问和操作该元素。通过迭代器,我们可以实现对容器的遍历、查找、修改等操作,大大增强了程序的灵活性和通用性。
声明迭代器
有迭代器的容器类型使用iterator和const_iterator类型来表示迭代器的类型
我们可以看个例子
vector<int>::iterator it1;
//it1能读取和修改vector<int>的元素string::iterator it2;
//it2能读取和修改string的元素vector<int>::const_iterator it3;
//it3能读取vector<int>的元素,不能修改string的元素string::const_iterator it4;
//it4能读取string的元素,不能修改string的元素
const_iterator的对象和常量指针差不多,能读取但是不能修改它所指元素的值。相反,iterator的对象可读可写。
如果vector和string对象是个常量,只能使用const_iterator;
如果vector和string对象不是常量,则既可以使用iterator也可以使用const_iterator
begin成员和end成员
begin和end 操作生成指向容器中第一个元素和尾元素之后位置的迭代器。
这两个迭代器最常见的用途是形成一个包含容器中所有元素的迭代器范围。
begin和end有多个版本:带r的版本返回反向迭代器;以c开头的版本则返回const迭代器:
list<string> a ={"Milton", "Shakespeare", "Austen"};
auto itl = a.begin(); // list<string>::iterator
auto it2 = a.rbegin(); // list<string>::reverse iterator
auto it3 = a.cbegin(); // list<string>::const iterator
auto it4 = a.crbegin();// list<string>::const reverse iterator
不以c开头的函数都是被重载过的。
也就是说,实际上有两个名为begin的成员。
一个是const成员,返回容器的const iterator类型。
另一个是非常量成员,返回容器的iterator类型。r
begin、end和rend的情况类似。当我们对一个非常量对象调用这些成员时,得到的是返回iterator的版本。只有在对一个const对象调用这些函数时,才会得到一个const版本。
与const指针和引用类似可以将一个普通的iterator转换为对应的const_iterator,但反之不行。
以c开头的版本是C++新标准引入的,用以支持auto与begin和end函数结合使用。过去,没有其他选择,只能显式声明希望使用哪种类型的迭代器:
//显式指定类型
list<string>::iterator it5 = a.begin();
在C++中,容器的end()
函数返回一个迭代器,指向容器中的最后一个元素的下一个位置。而begin()
函数返回一个迭代器,指向容器中的第一个元素的位置。
在使用迭代器遍历容器时,通常会将begin()
函数返回的迭代器作为起始位置,将end()
函数返回的迭代器作为结束位置。遍历过程中,迭代器会从起始位置逐个前进到结束位置,直到到达结束位置为止。
例如,对于一个vector<int>
容器,可以使用迭代器来遍历其中的元素:
vector<int> vec = {1, 2, 3, 4, 5};for (auto it = vec.begin(); it != vec.end(); ++it) {cout << *it << " ";
}
cout << endl;
在这个例子中,vec.begin()
返回一个迭代器,指向第一个元素1的位置,vec.end()
返回一个迭代器,指向最后一个元素5的下一个位置。循环中,首先将迭代器从起始位置1依次移动到2、3、4、5,最后到达结束位置,输出结果为
1 2 3 4 5
需要注意的是,end()
函数返回的迭代器实际上指向了容器的末尾,并不指向容器中的最后一个元素。这是一个常见的迭代器设计约定。
begin和end运算符的返回类型
begin和end运算符的返回类型取决于调用它的这个对象是否是常量
如果对象是常量,begin和end返回const_iterator,如果对象不是常量,begin和end返回iterator
vector<int> a;
const vector<int> cv;
auto it1=v.begin();
auto it2=cv.begin();
我们可以看到it1的类型是vector<int>::iterator,it2的类型是vector<int>::const_iterator
cbegin()和cend()函数——常量迭代器
为了便于专门得到const_iterator类型的返回值,C++11引入了两个新函数:cbegin()和cend()函数,
我们看看
vector<int> v;
auto it3=v.cbegin();
可以看到啊,it3是const_iterator类型
我们来详细介绍一下它们两个
在C++中,容器类提供了两个函数,即cbegin()和cend()函数,用于获取一个常量迭代器的起始和结束位置。
-
cbegin()函数返回一个指向容器中第一个元素的常量迭代器。这个迭代器只能用于读取元素的值,不能修改容器的内容。如果容器为空,cbegin()函数将返回一个指向末尾的常量迭代器。
-
cend()函数返回一个指向容器中最后一个元素之后位置的常量迭代器。这个迭代器只能用于判断循环结束的条件,不能访问迭代器指向的元素的值。
这两个函数适用于所有标准容器,如vector、list、set等,并且它们返回的迭代器类型都是const_iterator。
常见的用法是在for循环中使用cbegin()和cend()函数来遍历容器中的元素,例如:
std::vector<int> vec = {1, 2, 3, 4, 5};for (auto it = vec.cbegin(); it != vec.cend(); ++it) {std::cout << *it << " ";
}
注意:在使用cbegin()和cend()函数获取到的常量迭代器时,不能通过迭代器修改容器的值,只能读取元素的值。如果需要修改容器的值,需要使用普通迭代器,而不是常量迭代器。
使用迭代器
在C++中,可以使用迭代器来遍历容器中的元素。以下是使用迭代器的一般步骤:
-
定义一个迭代器变量,将其初始化为容器的起始位置。例如:
vector<int> vec = {1, 2, 3, 4, 5}; vector<int>::iterator it = vec.begin();
-
使用迭代器来访问容器中的元素。可以使用解引用操作符
*
来获取迭代器指向的元素值。例如:cout << *it << endl; // 输出第一个元素的值
-
可以使用++操作符将迭代器前进到容器的下一个元素。例如:
++it; // 前进到下一个元素
-
可以使用循环结构(如while、for)和条件判断来遍历整个容器。例如:
for (vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {cout << *it << " "; } cout << endl;
或者使用基于范围的for 循环:
for (int element : vec) {cout << element << " "; } cout << endl;
在使用迭代器遍历容器时,需要注意以下几点:
- 使用迭代器需要包含头文件<
iterator>
。 - 复制容器的迭代器可以使两个迭代器指向相同的位置。
- 不能在迭代器失效的情况下使用迭代器,例如在插入或删除元素后。
迭代器运算符
在C++中,迭代器提供了一些运算符来对迭代器进行操作和访问容器中的元素。以下是常用的迭代器运算符:
-
解引用运算符(*):用于获取迭代器指向位置的元素值。例如,*it 表示获取迭代器 it 指向位置的元素值。
-
自增运算符(++):用于将迭代器向前移动一个位置。例如,++it表示将迭代器 it 向前移动一个位置。
-
自减运算符(--):用于将迭代器向后移动一个位置。例如,--it表示将迭代器 it 向后移动一个位置。
-
箭头运算符(->):用于获取迭代器指向位置的成员变量或成员函数。例如,it->member 表示获取迭代器 it 指向位置的成员变量或成员函数。
-
等于运算符(==)和不等于运算符(!=):用于比较两个迭代器是否指向同一个位置。例如,it1 == it2 表示判断迭代器 it1 和 it2 是否指向同一个位置。
-
大于运算符(>)、小于运算符(<)、大于等于运算符(>=)和小于等于运算符(<=):用于比较两个迭代器指向位置的相对顺序。例如,it1 > it2 表示迭代器 it1 指向位置在迭代器 it2 指向位置之后。
需要注意的是,不是所有运算符对所有类型的迭代器都可用。有些运算符只适用于双向迭代器或随机访问迭代器,而不适用于单向迭代器。
迭代器的算术运算
在C++中,一些迭代器支持算术运算符,这些运算符可以用于在迭代器上进行加法和减法操作。这些运算符包括:
-
加法运算符(+):用于将迭代器向前移动指定的步数。例如,it + n 表示将迭代器 it 向前移动 n 个位置。
-
减法运算符(-):用于将迭代器向后移动指定的步数,或者计算两个迭代器之间的距离。例如,it - n 表示将迭代器 it 向后移动 n 个位置,it1 - it2 表示计算迭代器 it1 和 it2 之间的距离。
需要注意的是,不是所有类型的迭代器都支持算术运算符。只有随机访问迭代器支持算术运算符,而单向迭代器和双向迭代器不支持算术运算符。在使用算术运算符之前,应该确保迭代器的类型是随机访问迭代器。
此外,还有一些其他的运算符可以与迭代器一起使用,如赋值运算符(=)、复合赋值运算符(+=、-=)等。这些运算符可以用于更新迭代器的位置或将一个迭代器赋值给另一个迭代器。
需要注意的是,在进行算术运算时,应该确保迭代器不超出容器的边界。否则,可能会导致未定义的行为或错误。在使用迭代器进行算术运算时,应该始终谨慎处理边界情况,并确保迭代器始终指向有效的位置。
插入迭代器
如果有一个容器,我们预先不知道它的长度,如果要把元素添加到这个容器中,而不是覆盖已有内容,那该怎么办呢?接下来就要使用到插入迭代器了。这三者都需要头文件<iterator>
back_insert_iterator
back_insert_iterator
是一个插入迭代器适配器,用于在容器的末尾插入元素。它的用法如下:
- 创建
back_insert_iterator
对象并绑定到容器:
std::back_insert_iterator<std::vector<int>> backIt(vec);
这里我们创建了一个back_insert_iterator
对象backIt
,并将它绑定到一个std::vector<int>
容器vec
上。通过这个back_insert_iterator
对象,我们可以向vec
插入元素。
- 使用插入迭代器插入元素:
*backIt = 6;
backIt++;
*backIt = 7;
backIt++;
*backIt = 8;
我们可以通过解引用操作符*
来访问插入迭代器对应的容器,并将值赋给它。然后,使用递增操作符++
来移动插入迭代器的位置。这样,我们就可以在容器的末尾依次插入元素。
- 使用实例:
std::vector<int> vec;
std::back_insert_iterator<std::vector<int>> backIt(vec);*backIt = 1; // 插入元素1
backIt++;
*backIt = 2; // 插入元素2
backIt++;
*backIt = 3; // 插入元素3// 另一种更简洁的写法
std::vector<int> vec2;
std::back_insert_iterator<std::vector<int>> backIt2(vec2);std::fill_n(backIt2, 5, 42); // 在vec2末尾插入5个值为42的元素
需要注意的是,back_insert_iterator
是一个输出迭代器,只能用于写入操作,不能用于读取元素。
此外,它只能用于支持push_back
操作的容器,比如std::vector
和std::list
等。
insert_iterator
insert_iterator
是STL中的一个插入迭代器适配器,用于在容器的任意位置插入元素。它的用法如下:
- 创建
insert_iterator
对象并绑定到容器以及插入位置:
std::insert_iterator<std::vector<int>> insertIt(vec, vec.begin());
这里我们创建了一个insert_iterator
对象insertIt
,并将它绑定到一个std::vector<int>
容器vec
上,同时指定插入位置为vec.begin()
。通过这个insert_iterator
对象,我们可以在指定位置插入元素。
- 使用插入迭代器插入元素:
*insertIt = 6;
insertIt++;
*insertIt = 7;
insertIt++;
*insertIt = 8;
我们可以通过解引用操作符*
来访问插入迭代器对应的容器,并将值赋给它。然后,使用递增操作符++
来移动插入迭代器的位置。这样,我们就可以在指定位置插入元素。
- 使用实例:
std::vector<int> vec;
std::insert_iterator<std::vector<int>> insertIt(vec, vec.begin());*insertIt = 1; // 在vec的开始位置插入元素1
insertIt++;
*insertIt = 2; // 在vec的第二个位置插入元素2
insertIt++;
*insertIt = 3; // 在vec的第三个位置插入元素3// 另一种更简洁的写法
std::vector<int> vec2;
std::insert_iterator<std::vector<int>> insertIt2(vec2, vec2.begin());std::fill_n(insertIt2, 5, 42); // 在vec2的开始位置插入5个值为42的元素
需要注意的是,insert_iterator
是一个输出迭代器,只能用于写入操作,不能用于读取元素。此外,它可以用于支持insert
操作的容器,比如std::vector
和std::list
等。
front_insert_iterator
front_insert_iterator
是一个迭代器适配器,可以用于在容器的前端插入元素。它是C++标准库的一部分,可以用于修改向量、列表和双端队列等容器。
要使用front_insert_iterator
,需要包含<iterator>
头文件:
#include <iostream>
#include <iterator>
#include <vector>int main() {std::vector<int> myVector;std::front_insert_iterator<std::vector<int>> frontIt(myVector);*frontIt = 1; // 在向量的前端插入1frontIt++; // 迭代器前进// 或者,可以直接使用insert函数插入元素frontIt = std::insert_iterator<std::vector<int>>(myVector, myVector.begin(), 2);// 打印向量for (const auto& element : myVector) {std::cout << element << " ";}std::cout << std::endl;return 0;
}
在这个例子中,我们创建了一个名为myVector
的std::vector<int>
向量,以及一个名为frontIt
的front_insert_iterator
迭代器。我们使用frontIt
迭代器向向量中插入元素。
*frontIt = 1
语句在向量的前端插入值1
。frontIt++
语句将迭代器前进到下一个位置。
另外,你也可以使用insert_iterator(container, iterator, value)
的语法,其中container
是要修改的容器,iterator
是要插入值的位置,value
是要插入的值。
在代码中的frontIt = std::insert_iterator<std::vector<int>>(myVector, myVector.begin(), 2)
一行中演示了这种用法。
最后,我们打印出向量的元素,以验证结果。输出将是:
2 1
这表明通过front_insert_iterator
成功地在向量的前端插入了元素。
相关文章:

掘根宝典之C++迭代器简介
简介 迭代器是一种用于遍历容器元素的对象。它提供了一种统一的访问方式,使程序员可以对容器中的元素进行逐个访问和操作,而不需要了解容器的内部实现细节。 C标准库里每个容器都定义了迭代器 迭代器的作用类似于指针,可以指向容器中的某个…...

DP-力扣 120.三角形最小路径和
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。相邻的结点: 下标与上一层结点下标相同或者等于上一层结点下标 1 的两个结点。样例: 例如,给定三角形: [ [2], [3,4], [6,5,7], [4…...

【WEEK3】学习目标及总结【SpringMVC】【中文版】
学习目标: 三周完成SpringMVC入门——第三周 感觉这周很难完成任务了,大概率还会有第四周 学习内容: 参考视频教程【狂神说Java】SpringMVC最新教程IDEA版通俗易懂数据处理JSON交互处理 学习时间及产出: 第三周 MON~FRI 2024.…...

peft模型微调--Prompt Tuning
模型微调(Model Fine-Tuning)是指在预训练模型的基础上,针对特定任务进行进一步的训练以优化模型性能的过程。预训练模型通常是在大规模数据集上通过无监督或自监督学习方法预先训练好的,具有捕捉语言或数据特征的强大能力。 PEF…...

【算法训练营】周测1
清华大学驭风计划课程链接 学堂在线 - 精品在线课程学习平台 (xuetangx.com) 如果需要答案代码可以私聊博主 有任何疑问或者问题,也欢迎私信博主,大家可以相互讨论交流哟~~ 考题11-1 题目描述 有一个初始时为空的序列,你的任务是维护这个…...

PyTorch Dataset、DataLoader长度
pytorch 可以直接对 Dataset 对象用 len() 求数据集大小,而 DataLoader 对象也可以用 len(),不过求得的是用这个 loader 在一个 epoch 能有几多 iteration,容易混淆。本文记录几种情况的对比。 from torch.utils.data import Dataset, DataL…...

动态IP和静态IP
与静态 IP 地址不同,动态 IP 地址会定期更改。让我们来分析一下: 1. IP 地址基础知识: * IP 地址是一个数字标签,用于唯一标识网络上的每个设备。 * 当设备通过网络通信时,数据会在它们之间来回传输。每个数据包都标有…...

中电金信:技术实践|Flink维度表关联方案解析
导语:Flink是一个对有界和无界数据流进行状态计算的分布式处理引擎和框架,主要用来处理流式数据。它既可以处理有界的批量数据集,也可以处理无界的实时流数据,为批处理和流处理提供了统一编程模型。 维度表可以看作是用户来分析数…...

HQL 55 题【持续更新】
前言 今天开始为期一个多月的 HQL 练习,共 55 道 HQL 题,大概每天两道,从初级函数到中级函数。这次的练习不再是基础的 join 那种通用 SQL 语法了,而是引入了更多 Hive 的函数(单行函数、窗口函数等)。 我…...

lqb省赛日志[8/37]-[搜索·DFS·BFS]
一只小蒟蒻备考蓝桥杯的日志 文章目录 笔记DFS记忆化搜索 刷题心得小结 笔记 DFS 参考 深度优先搜索(DFS) 总结(算法剪枝优化总结) DFS的模板框架: function dfs(当前状态){if(当前状态 目的状态){}for(寻找新状态){if(状态合法){vis[访问该点];dfs(新状态);?…...

uni app 钓鱼小游戏
最近姑娘喜欢玩那个餐厅游戏里的钓鱼 ,经常让看广告,然后就点点点... 自己写个吧。小鱼的图片自己搞。 有问题自己改,不要私信我 <template><view class"page_main"><view class"top_linear"><v…...

openssl3.2 - note - Decoders and Encoders with OpenSSL
文章目录 openssl3.2 - note - Decoders and Encoders with OpenSSL概述笔记编码器/解码器的调用链OSSL_STORE 编码器/解码器的名称和属性OSSL_FUNC_decoder_freectx_fnOSSL_FUNC_encoder_encode_fn官方文档END openssl3.2 - note - Decoders and Encoders with OpenSSL 概述 …...

分享几个 Selenium 自动化常用操作
最近工作会用到selenium来自动化操作一些重复的工作,那么在用selenium写代码的过程中,又顺手整理了一些常用的操作,分享给大家。 常用元素定位方法 虽然有关selenium定位元素的方法有很多种,但是对于没有深入学习,尤…...

【Python】【数据类型】List (列表) 的常见操作
1. 创建 使用内置函数list()将字符串创建为列表 list1 [a, b, c, d] print(list1 , list1) # list1 [a, b, c, d] list1 list(abcd) print(list1) # [a, b, c, d]使用列表推导式创建列表 list1 [x for x in range(1, 10)] print(list1) # [1, 2, 3, 4, 5, 6, 7, 8, 9]多…...

【C语言】病人信息管理系统
本设计实现了一个病人信息管理系统,通过链表数据结构来存储和操作病人的信息。用户可以通过菜单选择录入病人信息、查找病人信息、修改病人信息、删除病人信息、查看所有病人信息和查看专家信息等操作,还可以根据病人的科室、姓名、性别和联系方式进行查找,以及支持修改病人…...

Java Spring Boot 接收时间格式的参数
报错 JSON parse error: Cannot deserialize value of type java.time.LocalDateTime from String “2024-03-14 12:30:00”: Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text ‘2024-03-14 12:30:00’ could not be parsed a…...

【C++】实现红黑树
目录 一、认识红黑树1.1 概念1.2 定义 二、实现红黑树2.1 插入2.2 与AVL树对比 一、认识红黑树 1.1 概念 红黑树是一个二叉搜索树,与AVL树相比,红黑树不再使用平衡因子来控制树的左右子树高度差,而是用颜色来控制平衡,颜色为红色…...

爬虫(六)
复习回顾: 01.浏览器一个网页的加载全过程1. 服务器端渲染html的内容和数据在服务器进行融合.在浏览器端看到的页面源代码中. 有你需要的数据2. 客户端(浏览器)渲染html的内容和数据进行融合是发生在你的浏览器上的.这个过程一般通过脚本来完成(javascript)我们通过浏览器可以…...

最长连续序列 - LeetCode 热题 3
大家好!我是曾续缘💝 今天是《LeetCode 热题 100》系列 发车第 3 天 哈希第 3 题 ❤️点赞 👍 收藏 ⭐再看,养成习惯 最长连续序列 给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素…...

运营模型—RFM 模型
运营模型—RFM 模型 RFM 是什么其实我们前面的文章介绍过,这里我们不再赘述,可以参考运营数据分析模型—用户分层分析,今天我们要做的事情是如何落地RFM 模型 我们的数据如下,现在我们就开始进行数据处理 数据预处理 因为数据预处理没有一个固定的套路,都是根据数据的实…...

YOLOv9|加入2023Gold YOLO中的GD机制!遥遥领先!
专栏介绍:YOLOv9改进系列 | 包含深度学习最新创新,助力高效涨点!!! 一、Gold YOLO摘要 在过去的几年里,YOLO系列模型已经成为实时目标检测领域的领先方法。许多研究通过修改体系结构、增加数据和设计新的损…...

WRF模型运行教程(ububtu系统)--III.运行WRF模型(官网案例)
零、创建DATA目录 # 1.创建一个DATA目录用于存放数据(一般为fnl数据,放在Build_WRF目录下)。 mkdir DATA # 2.进入 DATA cd DATA 一、WPS预处理 在模拟之前先确定模拟域(即模拟范围),并进行数据预处理(…...

html和winform webBrowser控件交互并播放视频(包含转码)
1、 为了使网页能够与winform交互 将com的可访问性设置为真 [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name "FullTrust")][System.Runtime.InteropServices.ComVisibleAttribute(true)] 2、在webBrow…...

Neo4j 批量导入数据 从官方文档学习LOAD CSV 命令 小白可食用版
学习LOAD CSV🚀 在使用Neo4j进行大量数据导入的时候,发现如果用代码自动一行一行的导入效率过低,因此明白了为什么需要用到批量导入功能,在Neo4j中允许批量导入CSV文件格式,刚开始从网上的中看了各种半残的博客或者视频…...

Day43-2-企业级实时复制intofy介绍及实践
Day43-2-企业级实时复制intofy介绍及实践 1. 企业级备份方案介绍1.1 利用定时方式,实现周期备份重要数据信息。1.2 实时数据备份方案1.3 实时复制环境准备1.4 实时复制软件介绍1.5 实时复制inotify机制介绍1.6 项目部署实施1.6.1 部署环境准备1.6.2 检查Linux系统支…...

2024年AI辅助研发趋势深度解析:科技革新与效率提升的双重奏
随着人工智能技术的迅猛发展,AI辅助研发正逐渐成为科技界和工业界的热门话题。特别是在2024年,这一趋势将更加明显,AI辅助研发将在各个领域展现出强大的潜力和应用价值。 首先,AI辅助研发将进一步提升研发效率。传统的研发模式往…...

bash: mysqldump: command not found
问题:在linux上执行mysql备份的时候,出现此异常 mysqldump命令找不到 解决: 1、找到mysql目录(找到mysql可执行命令目录) which mysql 有图可知,mysql安装在: /usr1/local/java/mysql 2、my…...

hcie数通和云计算选哪个好?
1. 基础知识与技能要求 数通技术是网络技术的核心,它涉及到网络协议、路由交换、网络安全等多个方面。如果你是一名网络工程师或开发者,想要在数通领域有所建树,你需要具备扎实的基础知识和丰富的实战经验。 云计算则更注重于虚拟化、存储、网…...

浅易理解:非极大抑制NMS
什么是非极大抑制NMS 非极大值抑制(Non-Maximum Suppression,简称NMS)是一种在计算机视觉和图像处理领域中广泛使用的后处理技术,特别是在目标检测任务中。它的主要目的是解决目标检测过程中出现的重复检测问题,即对于…...

C语言如何进⾏字符数组的复制?
一、问题 有两个字符数组a和b,a的值是“Good Bye” ,b的值是 “Bye Bye”,现在要把b 复制到a中,使a变成“Bye Bye”,应该怎么做? 二、解答 在字符串操作中,字符串复制是⽐较常⽤的操作之⼀。在…...