2-3容器的迭代器_容器迭代器_mingqian_chu的博客-程序员秘密

技术标签: # C++ STL  容器的迭代器  

1. 迭代器的产生

无论是序列容器还是关联容器,最常做的操作无疑是遍历容器中存储的元素,而实现此操作,多数情况会选用“迭代器(iterator)”来实现。那么,迭代器到底是什么呢?

我们知道,尽管不同容器的内部结构各异,但它们本质上都是用来存储大量数据的,换句话说,都是一串能存储多个数据的存储单元。因此,诸如数据的排序、查找、求和等需要对数据进行遍历的操作方法应该是类似的。

既然类似,完全可以利用泛型技术,将它们设计成适用所有容器的通用算法,从而将容器和算法分离开。但实现此目的需要有一个类似中介的装置,它除了要具有对容器进行遍历读写数据的能力之外,还要能对外隐藏容器的内部差异,从而以统一的界面向算法传送数据。

这是泛型思维发展的必然结果,于是迭代器就产生了。简单来讲,迭代器和 C++ 的指针非常类似,它可以是需要的任意类型,通过迭代器可以指向容器中的某个元素,如果需要,还可以对该元素进行读/写操作。

2. 不同容器对应的迭代器

STL 标准库为每一种标准容器定义了一种迭代器类型,这意味着,不同容器的迭代器也不同,其功能强弱也有所不同。

容器的迭代器的功能强弱,决定了该容器是否支持 STL 中的某种算法。

常用的迭代器按功能强弱分为输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器 5 种。

本节主要介绍后面的这 3 种迭代器。

输入迭代器和输出迭代器比较特殊,它们不是把数组或容器当做操作对象,而是把输入流/输出流作为操作对象。

  1. 前向迭代器(forward iterator)
    假设 p 是一个前向迭代器,则 p 支持 ++p,p++,*p 操作,还可以被复制或赋值,可以用 == 和 != 运算符进行比较。此外,两个正向迭代器可以互相赋值。

  2. 双向迭代器(bidirectional iterator)
    双向迭代器具有正向迭代器的全部功能,除此之外,假设 p 是一个双向迭代器,则还可以进行 --p 或者 p-- 操作(即一次向后移动一个位置)。

  3. 随机访问迭代器(random access iterator)
    随机访问迭代器具有双向迭代器的全部功能。除此之外,假设 p 是一个随机访问迭代器,i 是一个整型变量或常量,则 p 还支持以下操作:


p+=i:使得 p 往后移动 i 个元素。
p-=i:使得 p 往前移动 i 个元素。
p+i:返回 p 后面第 i 个元素的迭代器。
p-i:返回 p 前面第 i 个元素的迭代器。
p[i]:返回 p 后面第 i 个元素的引用。

此外,两个随机访问迭代器 p1、p2 还可以用 <、>、<=、>= 运算符进行比较。另外,表达式 p2-p1 也是有定义的,其返回值表示 p2 所指向元素和 p1 所指向元素的序号之差(也可以说是 p2 和 p1 之间的元素个数减一)。

表 1 所示,是 C++ 11 标准中不同容器指定使用的迭代器类型。

表 1 不同容器的迭代器

容器 对应的迭代器类型
array 随机访问迭代器
vector 随机访问迭代器
deque 随机访问迭代器
list 双向迭代器
set / multiset 双向迭代器
map / multimap 双向迭代器
forward_list 前向迭代器
unordered_map / unordered_multimap 前向迭代器
unordered_set / unordered_multiset 前向迭代器
stack 不支持迭代器
queue 不支持迭代器

注意,容器适配器 stack 和 queue 没有迭代器,
它们包含有一些成员函数,可以用来对元素进行访问。

3. 迭代器的定义方式

尽管不同容器对应着不同类别的迭代器,但这些迭代器有着较为统一的定义方式,具体分为 4 种,如表 1 所示。

表 2 迭代器的 4 种定义方式

迭代器定义方式 具体格式
正向迭代器 容器类名::iterator 迭代器名;
常量正向迭代器 容器类名::const_iterator 迭代器名;
反向迭代器 容器类名::reverse_iterator 迭代器名;
常量反向迭代器 容器类名::const_reverse_iterator 迭代器名;

值得一提的是,表 2 中的反向迭代器全称为 “反向迭代器适配器”,后续章节会做详细讲解,这里读者只需要知道其用法即可。

通过定义以上几种迭代器,就可以读取它指向的元素,*迭代器名就表示迭代器指向的元素。其中,常量迭代器和非常量迭代器的分别在于,通过非常量迭代器还能修改其指向的元素。另外,反向迭代器和正向迭代器的区别在于:
对正向迭代器进行 ++ 操作时,迭代器会指向容器中的后一个元素;
而对反向迭代器进行 ++ 操作时,迭代器会指向容器中的前一个元素。

注意,以上 4 种定义迭代器的方式,并不是每个容器都适用。有一部分容器同时支持以上 4 种方式,比如 array、deque、vector;而有些容器只支持其中部分的定义方式,例如 forward_list 容器只支持定义正向迭代器,不支持定义反向迭代器。
具体容器支持定义迭代器的方式,讲具体容器时会详细说明。另外,读者也可以通过 C++ STL标准手册,查询具体容器迭代器支持的定义方式。

3. 迭代器的举例

3.1 vector容器的迭代器举例

下面就以 vector 容器为例,带领大家实际感受迭代器的用法和功能。通过前面的学习,vector 支持随机访问迭代器,因此遍历 vector 容器有以下几种做法。下面的程序中,每个循环演示了一种做法:

//遍历 vector 容器。
#include <iostream>
//需要引入 vector 头文件
#include <vector>
using namespace std;
int main()
{
    
    vector<int> v{
    1,2,3,4,5,6,7,8,9,10}; //v被初始化成有10个元素
    cout << "第一种遍历方法:" << endl;
    //size返回元素个数
    for (int i = 0; i < v.size(); ++i)
        cout << v[i] <<" "; //像普通数组一样使用vector容器
    //创建一个正向迭代器,当然,vector也支持其他 3 种定义迭代器的方式
    
       cout << endl << "第二种遍历方法:" << endl;
       vector<int>::iterator i;
    //用 != 比较两个迭代器
    for (i = v.begin(); i != v.end(); ++i)
        cout << *i << " ";
    
       cout << endl << "第三种遍历方法:" << endl;
    for (i = v.begin(); i < v.end(); ++i) //用 < 比较两个迭代器
        cout << *i << " ";
   
       cout << endl << "第四种遍历方法:" << endl;
    i = v.begin();
    while (i < v.end()) {
     //间隔一个输出
        cout << *i << " ";
        i += 2; // 随机访问迭代器支持 "+= 整数"  的操作
    }
}

运行结果为:
第一种遍历方法:
1 2 3 4 5 6 7 8 9 10
第二种遍历方法:
1 2 3 4 5 6 7 8 9 10
第三种遍历方法:
1 2 3 4 5 6 7 8 9 10
第四种遍历方法:
1 3 5 7 9

3.2 list容器的迭代器举例

再举一个例子,我们知道,list 容器的迭代器是双向迭代器。假设 v 和 i 的定义如下:

//创建一个 v list容器
list<int> v;
//创建一个常量正向迭代器,同样,list也支持其他三种定义迭代器的方式。
list<int>::const_iterator i;
则以下代码是合法的:
for(i = v.begin(); i != v.end(); ++i)
    cout << *i;
以下代码则不合法,因为双向迭代器不支持用“<”进行比较:
for(i = v.begin(); i < v.end(); ++i)
    cout << *i;
以下代码也不合法,因为双向迭代器不支持用下标随机访问元素:
for(int i=0; i<v.size(); ++i)
    cout << v[i];

3.3 数组容器的迭代器

在 C++ 中,数组也是容器。

数组的迭代器就是指针,而且是随机访问迭代器。例如,对于数组 int a[10],int * 类型的指针就是其迭代器。则 a、a+1、a+2 都是 a 的迭代器。另外,以上有关 vector、list 容器的具体用法,后续章节会做详细讲解。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/chumingqian/article/details/124236195

智能推荐

cisco思科网络设备的MIB信息访问_cisco mib_一下子就醒了的博客-程序员秘密

一、实验目的本实验的主要目的是学习SNMP服务在交换机和路由器的配置,以及用MIB浏览器访问交换机和路由器的的MIB对象的值,重点了解其相应的网络管理对象。并通过SNMP来实现监控和配置网络设备。二、实验内容1、交换机和路由器SNMP配置;2、通过SNMP来实现监控和配置网络设备。三、实验工具Packet tracer模拟器。四、实验环境                              ...

Python3+OpenCV3+Pycharm编程:添加高斯噪声与高斯模糊_Earlybird丶的博客-程序员秘密

高斯模糊:高斯滤波是一种线性平滑低通滤波器,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。滤波高斯就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。用一个模板(或称卷积,掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。添加高斯噪声def clamp(pv): """防止溢出"""...

版本控制(上)——Git使用教程_ifengouy的博客-程序员秘密

1、Git简介 Git是用于Linux内核开发的版本控制工具,它采用了分布式版本库的作法,不需要服务器端软件,就可以运作版本控制,使得源代码的发布和交流极其方便。Git基本交互流程图如下: 使用Git进行版本控制有两种方式,一是使用msysgit命令行方式,另一种则是使用图形化工具TortoiseGit,但两种方式都需安装msysgit。1)msysgit是Windows版

最全的50道Redis面试题_redis touch_FeelTouch Labs的博客-程序员秘密

1、什么是Redis?Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。 Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构...

Talib技术因子详解(八)_Coding的叶子的博客-程序员秘密

talib安装方式:pip install Ta-libTushare数据获取请参考:金融量化分析基础环境搭建数据获取代码请参考:Talib技术因子详解(一)26、MACD 异同移动平均线,调用方式如下:macd, macdsignal, macdhist = talib.MACD(close, fastperiod, slowperiod, signalperiod)当MACD升穿Signal Line,入市讯息;当MACD跌穿Signal Line,出市讯息。当MACD上升时,股票价

DispatchMessage, SendMessage_nongfu_spring的博客-程序员秘密

SendMessage实现sendmessage发送消息的接收,在消息的接收方,覆写DefWindowProc(),在该方法中即可接收到sendmessage方法发送来的消息。因为sendmessage发送的消息,不再经过消息队列,而是直接发送给指定对象。所以一般的消息响应,包括PreTranslateMessage方法都无法接收到该信息,只能通过覆写DefWindowProc方法,来接收信息

随便推点

Android开发Tips(2)_SpikeKing的博客-程序员秘密

欢迎Follow我的GitHub, 关注我的CSDN.我会介绍关于Android的一些有趣的小知识点. 上一篇.1. Dagger2的开发顺序Module -&gt; Component -&gt; Application 首先模块(Module)创建需要提供的类实例, 其次把模块添加到组件(Component)中并提供需要注入的类, 最后把组件添加到应用(Application)中并提供接口.// 模块

layui 初始化form表单单选按钮无法选中的问题_Grass_allan的博客-程序员秘密

&lt;div class="layui-input-inline"&gt; &lt;input type="radio" name="toUrlType" value="1" title="页面跳转"&gt; &lt;input type="radio" name="toUrlType" value="2" title="外链" checked=...

【UE4/C++】切换镜头_ue4 c++ 切换镜头_云梦别的博客-程序员秘密

在类的头文件里添加#include "Kismet/GameplayStatics.h"Tick中的关键代码如下: TimeToNextCameraChange -= DeltaTime; if (TimeToNextCameraChange <= 0 ) { TimeToNextCameraChange += TimeIntervalBetweenCameraChange;

Cmake学习详细笔记(8) ——自定义编译选项_cmake自定义编译过程_呆呆狗meng的博客-程序员秘密

案例四:自定义编译选项1). 源文件结构.├── CmakeLists.txt├── config.h.in├── main.cpp└── students├── CmakeLists.txt├── student_pub.cpp└── student_pub.h2) .编写 CMakeLists.txt#cmake mini ~vercmake_minimum_required(VERSION 2.8)#project infoproject(Studdent3)messa

启用 VT-x/AMD-V 支持_paracici的博客-程序员秘密

在virtualbox虚拟机中安装ubuntu系统,提示“VT-x/AMD-V硬件加速已被启用,但当前处于无效状态····请确认在您的电脑的BIOS中以启用VT-x/AMD-V支持”。原因在于没有在主机中设置支持虚拟设备。重启主机,进入BIOS设置界面(开机按delete),选择BIOS--***--inter virtualization tech,选定为enable。保存退出即可(中间的路

【STM32+cubemx】0026 HAL库开发:NRF24L01无线2.4G通信模块的应用_xiaobaibai_2021的博客-程序员秘密

NRF24L01是NORDIC公司生产的一款无线通信通信芯片,可以工作在免费开放的2.4GHz频段;通信速率可以达到最高2Mbps;MUC可以使用SPI接口与它交互。本节我们就来使用stm32驱动NRF24L01实现无线通信,先实现简单的一对一通信,然后讲解一对多通信,最后实现在ack中返回数据的应用。1)NRF24L01模块硬件介绍直接使用nrf24L01芯片搭建电路需要比较高的射频功底,一般情况下推荐使用现成的电路模块,本文使用的是下图这种:类似的nrf24L01模块的对外引

推荐文章

热门文章

相关标签