掘根宝典之C++迭代器简介

在C++中,容器是一种用于存储和管理数据的数据结构。C++标准库提供了多种容器,每种容器都有其独特的特点和适用场景。

我们知道啊,我们可以通过下标运算符来对容器内的元素进行访问,但是只有少数几种容器才同时支持下标运算符([ ]),因此我们需要一个更通用的机制来帮我们实现对容器内元素的访问

简介

迭代器是一种用于遍历容器元素的对象。它提供了一种统一的访问方式,使程序员可以对容器中的元素进行逐个访问和操作,而不需要了解容器的内部实现细节。

C++标准库里每个容器都定义了迭代器

迭代器的作用类似于指针,可以指向容器中的某个元素,并通过操作迭代器来访问和操作该元素。通过迭代器,我们可以实现对容器的遍历、查找、修改等操作,大大增强了程序的灵活性和通用性。

迭代器的五种类型

在C++中,迭代器按照功能和特性可以分为五种类型:

  1. 输入迭代器(Input Iterator):只能读取容器中的元素值,且只能单向移动,不支持修改元素。可以使用++操作符前进,但不能使用--操作符后退。

  2. 输出迭代器(Output Iterator):只能写入容器中的元素值,且只能单向移动,不支持读取元素。可以使用++操作符前进,但不能使用--操作符后退。

  3. 正向迭代器(Forward Iterator):支持读取和修改容器中的元素值,且可以单向移动。可以使用++操作符前进,但不能使用--操作符后退。

  4. 双向迭代器(Bidirectional Iterator):支持读取和修改容器中的元素值,且可以双向移动。可以使用++和--操作符前进和后退。

  5. 随机访问迭代器(Random Access Iterator):支持读取和修改容器中的元素值,且可以随机访问容器中的元素。可以使用++、--、+、-、[]等操作符进行前进、后退和随机访问。

这些迭代器类型提供了不同级别的功能和灵活性,可以根据具体需求选择合适的迭代器类型。通常来说,使用更高级别的迭代器可以获得更多的功能和性能,但也需要考虑容器类型支持的迭代器类型。

注意:各种迭代器的类型都不是确定的,只是一种概念性描述。它们都有一个共同的名字iterator或者const_iterator,这个我们下面会讲到

begin()成员和end()成员 

和指针不一样的是,迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员

在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()函数返回的迭代器实际上指向了容器的末尾,并不指向容器中的最后一个元素。这是一个常见的迭代器设计约定。

迭代器类型

有迭代器的容器类型使用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返回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()函数,用于获取一个常量迭代器的起始和结束位置。

  1. cbegin()函数返回一个指向容器中第一个元素的常量迭代器。这个迭代器只能用于读取元素的值,不能修改容器的内容。如果容器为空,cbegin()函数将返回一个指向末尾的常量迭代器。

  2. 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++中,可以使用迭代器来遍历容器中的元素。以下是使用迭代器的一般步骤:

  1. 定义一个迭代器变量,将其初始化为容器的起始位置。例如:

    vector<int> vec = {1, 2, 3, 4, 5};
    vector<int>::iterator it = vec.begin();
    

  2. 使用迭代器来访问容器中的元素。可以使用解引用操作符*来获取迭代器指向的元素值。例如:

    cout << *it << endl;  // 输出第一个元素的值
    

  3. 可以使用++操作符将迭代器前进到容器的下一个元素。例如:

    ++it;  // 前进到下一个元素
    

  4. 可以使用循环结构(如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++中,迭代器提供了一些运算符来对迭代器进行操作和访问容器中的元素。以下是常用的迭代器运算符:

  1. 解引用运算符(*):用于获取迭代器指向位置的元素值。例如,*it 表示获取迭代器 it 指向位置的元素值。

  2. 自增运算符(++):用于将迭代器向前移动一个位置。例如,++it表示将迭代器 it 向前移动一个位置。

  3. 自减运算符(--):用于将迭代器向后移动一个位置。例如,--it表示将迭代器 it 向后移动一个位置。

  4. 箭头运算符(->):用于获取迭代器指向位置的成员变量或成员函数。例如,it->member 表示获取迭代器 it 指向位置的成员变量或成员函数。

  5. 等于运算符(==)和不等于运算符(!=):用于比较两个迭代器是否指向同一个位置。例如,it1 == it2 表示判断迭代器 it1 和 it2 是否指向同一个位置。

  6. 大于运算符(>)、小于运算符(<)、大于等于运算符(>=)和小于等于运算符(<=):用于比较两个迭代器指向位置的相对顺序。例如,it1 > it2 表示迭代器 it1 指向位置在迭代器 it2 指向位置之后。

需要注意的是,不是所有运算符对所有类型的迭代器都可用。有些运算符只适用于双向迭代器或随机访问迭代器,而不适用于单向迭代器。

迭代器的算术运算

在C++中,一些迭代器支持算术运算符,这些运算符可以用于在迭代器上进行加法和减法操作。这些运算符包括:

  1. 加法运算符(+):用于将迭代器向前移动指定的步数。例如,it + n 表示将迭代器 it 向前移动 n 个位置。

  2. 减法运算符(-):用于将迭代器向后移动指定的步数,或者计算两个迭代器之间的距离。例如,it - n 表示将迭代器 it 向后移动 n 个位置,it1 - it2 表示计算迭代器 it1 和 it2 之间的距离。

需要注意的是,不是所有类型的迭代器都支持算术运算符。只有随机访问迭代器支持算术运算符,而单向迭代器和双向迭代器不支持算术运算符。在使用算术运算符之前,应该确保迭代器的类型是随机访问迭代器。

此外,还有一些其他的运算符可以与迭代器一起使用,如赋值运算符(=)、复合赋值运算符(+=、-=)等。这些运算符可以用于更新迭代器的位置或将一个迭代器赋值给另一个迭代器。

需要注意的是,在进行算术运算时,应该确保迭代器不超出容器的边界。否则,可能会导致未定义的行为或错误。在使用迭代器进行算术运算时,应该始终谨慎处理边界情况,并确保迭代器始终指向有效的位置。

插入迭代器

如果有一个容器,我们预先不知道它的长度,如果要把元素添加到这个容器中,而不是覆盖已有内容,那该怎么办呢?接下来就要使用到插入迭代器了。这三者都需要头文件<iterator>

back_insert_iterator

back_insert_iterator是一个插入迭代器适配器,用于在容器的末尾插入元素。它的用法如下:

  1. 创建back_insert_iterator对象并绑定到容器:
std::back_insert_iterator<std::vector<int>> backIt(vec);

这里我们创建了一个back_insert_iterator对象backIt,并将它绑定到一个std::vector&lt;int>容器vec上。通过这个back_insert_iterator对象,我们可以向vec插入元素。

  1. 使用插入迭代器插入元素:
*backIt = 6;
backIt++;
*backIt = 7;
backIt++;
*backIt = 8;

我们可以通过解引用操作符*来访问插入迭代器对应的容器,并将值赋给它。然后,使用递增操作符++来移动插入迭代器的位置。这样,我们就可以在容器的末尾依次插入元素。

  1. 使用实例:
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::vectorstd::list等。

insert_iterator

insert_iterator是STL中的一个插入迭代器适配器,用于在容器的任意位置插入元素。它的用法如下:

  1. 创建insert_iterator对象并绑定到容器以及插入位置:
std::insert_iterator<std::vector<int>> insertIt(vec, vec.begin());

这里我们创建了一个insert_iterator对象insertIt,并将它绑定到一个std::vector&lt;int>容器vec上,同时指定插入位置为vec.begin()。通过这个insert_iterator对象,我们可以在指定位置插入元素。

  1. 使用插入迭代器插入元素:
*insertIt = 6;
insertIt++;
*insertIt = 7;
insertIt++;
*insertIt = 8;

我们可以通过解引用操作符*来访问插入迭代器对应的容器,并将值赋给它。然后,使用递增操作符++来移动插入迭代器的位置。这样,我们就可以在指定位置插入元素。

  1. 使用实例:
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::vectorstd::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;
}

在这个例子中,我们创建了一个名为myVectorstd::vector&lt;int>向量,以及一个名为frontItfront_insert_iterator迭代器。我们使用frontIt迭代器向向量中插入元素。

*frontIt = 1语句在向量的前端插入值1frontIt++语句将迭代器前进到下一个位置。

另外,你也可以使用insert_iterator(container, iterator, value)的语法,其中container是要修改的容器,iterator是要插入值的位置,value是要插入的值。

在代码中的frontIt = std::insert_iterator&lt;std::vector&lt;int>>(myVector, myVector.begin(), 2)一行中演示了这种用法。

最后,我们打印出向量的元素,以验证结果。输出将是:

2 1

这表明通过front_insert_iterator成功地在向量的前端插入了元素。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的商品识别系统(深度学习+UI界面+训练数据集+Python代码)

摘要&#xff1a;在零售行业的技术进步中&#xff0c;开发商品识别系统扮演着关键角色。本博文详细阐述了如何利用深度学习技术搭建一个高效的商品识别系统&#xff0c;并分享了一套完整的代码实现。系统采用了性能强劲的YOLOv8算法&#xff0c;同时对YOLOv7、YOLOv6、YOLOv5等…

Linux操作系统与Windows文件互传(FTP)

一、开启Ubuntu下的FTP服务 打开Ubuntu的终端窗口&#xff0c;然后执行如下命令来安装 FTP服务。 sudo apt-get install vsftpd等待软件安装完成后&#xff0c;用输入以下命令打开vsftpd.conf文件 sudo vim /etc/vsftpd.conf 找到下图的两个使能语句改成如图即可(记住保存后再…

Beans模块之工厂模块BeanFactoryAware

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

专题二 - 滑动窗口 - leetcode 904. 水果成篮 | 中等难度

leetcode 904. 水果成篮 leetcode 904. 水果成篮 | 中等难度1. 题目详情1. 原题链接2. 基础框架 2. 解题思路1. 题目分析2. 算法原理3. 时间复杂度 3. 代码实现4. 知识与收获 leetcode 904. 水果成篮 | 中等难度 1. 题目详情 你正在探访一家农场&#xff0c;农场从左到右种植…

比特币普通地址、隔离见证(兼容)、隔离见证(原生)、Taproot 地址傻傻分不清楚

我们在使用比特币钱包的时候&#xff0c;可以看到各种地址类型&#xff1a;普通地址、隔离见证&#xff08;兼容&#xff09;、隔离见证&#xff08;原生&#xff09;、Taproot 地址。 看得我们一脸懵逼&#xff0c;为什么会有这么多种类型的地址&#xff1f; 它们之间都有什么…

C语言 指针(4) qsort函数

目录 前言 一、回调函数 二、qsort函数 2.1 使用qsort函数排序整型数据 2.2 使用qsort排序结构数据 三、qsort函数的模拟实现 总结 前言 今天我们主要来学习一下C语言中的qsort排序函数。 一、回调函数 回调函数就是⼀个通过函数指针调用的函数。 如果你把函数的指针&a…

spring启动时如何自定义日志实现

一、现象 最近在编写传统的springmvc项目时&#xff0c;遇到了一个问题&#xff1a;虽然在项目的web.xml中指定了log4j的日志启动监听器Log4jServletContextListener&#xff0c;且开启了日志写入文件&#xff0c;但是日志文件中只记录业务代码中我们声明了日志记录器的日志&a…

力扣98、530、501-java刷题笔记

一、98. 验证二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 1.1题目 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左 子树 只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点…

[leetcode~dfs]1261. 在受污染的二叉树中查找元素

给出一个满足下述规则的二叉树&#xff1a; root.val 0 如果 treeNode.val x 且 treeNode.left ! null&#xff0c;那么 treeNode.left.val 2 * x 1 如果 treeNode.val x 且 treeNode.right ! null&#xff0c;那么 treeNode.right.val 2 * x 2 现在这个二叉树受到「污…

C#四部曲(知识补充)

Unity跨平台原理 .Net相关 只要编写的时候遵循.NET的这些规则&#xff0c;就能在.NET平台下通用 各种源码→根据.NET规范编写→(虚拟机)生成CIL中间码(保存在程序集中)→转成操作系统原代码 跨语言← 跨平台↓ Unity跨平台原理&#xff08;Mono&#xff09; c#脚本→MonoC#编…

MySQL 事务的原理以及长事务的预防和处置

transaction_isolation 隔离级别 读未提交 读提交 视图是在每个 SQL 语句开始执行的时候创建的 可重复读 视图是在事务启动时创建的&#xff0c;整个事务存在期间都用这个视图 串行化…

安装OneNote for Win10 | Win10/Win11

前言 PC端的OneNote分为2个版本&#xff0c;分别是Microsoft Store版本和Office版本&#xff0c;Microsoft Store版本即为OneNote for Win10&#xff0c;此版的OneNote有最近笔记功能&#xff0c;但检索功能不如Office版本&#xff0c;个人认为2个版本各有优劣。 但OneNote f…

【硬件基础】电容的选型

1、电容的理论基础 电容器的本质就是储能&#xff0c;充放电 根据作用可分为&#xff1a;滤波电容&#xff0c;旁路电容&#xff0c;耦合电容&#xff0c;退耦电容&#xff0c;自举电容 2、电容的取值 计算取值&#xff0c;查手册&#xff0c;经验取值 3、电容的选取 分为铝…

“删边“的并查集------反向并查集

目录 1.题目2.思路3.代码 默认大家都会并查集了 1.题目 小美认为&#xff0c;在人际交往中&#xff0c;但是随着时间的流逝&#xff0c;朋友的关系也是会慢慢变淡的&#xff0c;最终朋友关系就淡忘了。 现在初始有一些朋友关系&#xff0c;存在一些事件会导致两个人淡忘了他们…

AssetBundle打包与加载

官方文档 参照视频 1.AssetBundle打包 1.1设置资源的命名和后缀 命名只支持小写 1.2创建Editor文件夹&#xff0c;在里面创建编辑器打包AssetBundle的脚本 using UnityEditor; using System.IO;public class CreateAssetBundles {[MenuItem("Assets/Build AssetBun…

旅游景区公共广播 园区广播 公路服务区广播

旅游景区公共广播 园区广播 公路服务区广播 旅游景区公共广播 旅游景区公共广播(又称背景音乐)简称BGM&#xff0c;它的主要作用是掩盖噪声并创造一种轻松和谐的气氛&#xff0c;是一种创造轻松愉快环境气氛的音乐。掩盖环境噪声&#xff0c;创造与旅游景区相适应的气氛&#…

遥感云计算的一个拐点

GeoForge&#xff0c;一个值得关注的遥感大数据应用 简介 GeoForge是由Ageospatial公司开发的一个基于大语言模型(GeoLLMs)的地理空间分析平台。GeoForg的目的是使每个人都可以轻松进行地图绘制和地理空间分析&#xff0c;无论您是外行还是专家。 Geo for ChatGPT 作者团队已…

07-java基础-锁之AQSReentrantLockBlockingQueueCountDownLatchSemapho

文章目录 0&#xff1a;AQS简介-常见面试题AQS具备特性state表示资源的可用状态AQS定义两种资源共享方式AQS定义两种队列自定义同步器实现时主要实现以下几种方法&#xff1a;同步等待队列条件等待队列 1&#xff1a;AQS应用之ReentrantLockReentrantLock如何实现synchronized不…

双环PID控制详细讲解

参考博客&#xff1a; &#xff08;1&#xff09;PID双环控制&#xff08;速度环和位置环&#xff09; &#xff08;2&#xff09;PID控制&#xff08;四&#xff09;&#xff08;单环与双环PID&#xff09; &#xff08;3&#xff09;内外双环pid算法 0 单环PID 目标位置→系…

阿里云第一次面试记录

java多态&#xff1f; 多态表示一个对象具有多种的状态&#xff0c;具体表现为父类的引用指向子类的实例 Fu f Zi z(); 多态是同一个行为具有多个不同表现形式或形态的能力。 多态就是同一个接口&#xff0c;使用不同的实例而执行不同操作 特点&#xff1a; 对象类型和引用类型…