C++之STL迭代器

2023-05-16

一、背景

迭代器(iterator)是一种抽象的设计理念,即迭代器模式,通过迭代器可以在不了解容器内部原理的情况下遍历容器。除此之外,STL中迭代器一个最重要的作用就是作为容器(vector,queue,list,map等)与STL算法的粘结剂,只要容器提供迭代器的接口,同一套算法代码可以利用在完全不同的容器中。

二、使用迭代器

如下所示的代码演示了迭代器是如何将容器和算法结合在一起的,其中使用了三种不同的容器,begin()和end()方法返回一个指向容器第一个元素和指向容器最后一个元素后面一个位置的迭代器。对于不同的容器,我们都使用同一个find函数。原因就在于find函数的实现无需考虑容器的种类,只需要容器传入的begin()和end() 迭代器能够完成标准迭代器的要求即可。

#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <algorithm>

int main(int argc, char *argv[])
{
	const int arraysize = 7;
	int ia[arraysize] = { 0, 1, 2, 3, 4, 5, 6 };
	vector<int > ivect(ia, ia + arraysize);
	list <int>   ilist(ia, ia + arraysize);
	deque<int> ideque(ia, ia + arraysize);
    
    // 对vector使用find
	vector<int>::iterator itl = find(ivect.begin(), ivect.end(), 4);
	if (itl == ivect.end())
	{
		std::cout << "vector ,4 is not find" << endl;
	}
	else
	{
		std::cout << "vector ,4 is find" << endl;
	}

    // 对list使用find
	list<int>::iterator it2 = find(ilist.begin(), ilist.end(), 4);
	if (it2 == ilist.end())
	{
		std::cout << "list ,4 is not find" << endl;
	}
	else
	{
		std::cout << "list ,4 is find" << endl;
	}

    // 对deque使用find
	deque<int>::iterator it3 = find(ideque.begin(), ideque.end(), 8);
	if (it3 == ideque.end())
	{
		std::cout << "deque ,8 is not find" << endl;
	}
	else
	{
		std::cout << "deque ,8 is find" << endl;
	}

    system("pause");
    return 0;
}

 

 三、迭代器的分类

常用的迭代器分为5个类别

    Input itertor (输入迭代器) 只读;只支持自增运算
    Output itertor(输出迭代器)只写;只支持自增运算
    Forward itertor(前向迭代器)读写;只支持自增运算
    Bidirectional itertor(双向迭代器)读写;支持自增和自减
    Random access itertor(随机访问迭代器)读写;支持完整迭代器算数运算

这五种迭代器的继承关系如下所示。

3.1 输入迭代器

    输入迭代器可用于读取容器中的元素,但是不保证能支持容器的写入操作。输入迭代器必须至少提供下列支持:

  •     相等和不等操作符(==,!=),比较两个迭代器。
  •     前置和后置的自增运算(++),使迭代器向前递进指向下一个元素。
  •     用于读取元素的解引用操作符(*),此操作符只能出现在赋值运算的右操作数上。
  •     箭头操作符(->),这是 (*it).member 的同义语,也就是说,对迭代器进行解引用来获取其所关联的对象的成员。

    输入迭代器只能顺序使用,一旦输入迭代器自增了,就无法再用它检查之前的元素。要求在这个层次上提供支持的泛型算法包括 find 和 accumulate,标准库 istream_iterator 类型输入迭代器。

3.2 输出迭代器

 输出迭代器可视为与输入迭代器功能互补的迭代器, 输出迭代器可用于向容器写入元素,但是不保证能支持读取容器内容。输出迭代器要求:

  • 前置和后置的自增运算(++),使迭代器向前递进指向下一个元素。
  •  解引用操作符(*),该引操作符只能出现在赋值运算的左操作数上。给解引用的输出迭代器赋值,将对该迭代器所指向的元素做写入操作。
  •  输出迭代器可以要求每个迭代器的值必须正好写入一次。使用输出迭代器时,对于指定的迭代器值应该使用一次 * 运算,而且只能用一次。

输出迭代器一般用作算法的第三个实参, 标记起始写入的位置。 例如, copy 算法使用一个输出迭代器作为它的第三个实参, 将输入范围内的元素复制到输出迭代器指定的目标位置。标准库 ostream_iterator 类型输出迭代器。

3.3 前向迭代器

  •     前向迭代器用于读写指定的容器。这类迭代器只会以一个方向遍历序列。

    前向迭代器支持输入迭代器和输出迭代器提供的所有操作,除此之外,还支持对同一个元素的多次读写。可复制前向迭代器来记录序列中的一个位置,以便将来返回此处。需要前向迭代器的泛型算法包括 replace。

3.4 双向迭代器

  •     双向迭代器从两个方向读写容器。除了提供前向迭代器的全部操作之外,双向迭代器还提供前置和后置的自减运算(–)。

    需要使用双向迭代器的泛型算法包括 reverse。所有标准库容器提供的迭代器都至少达到双向迭代器的要求。

3.5 随机访问迭代器

    随机访问迭代器提供在常量时间内访问容器任意位置的功能。这种迭代器除了支持双向迭代器的所有功能之外,还支持下面的操作:

  •     关系操作符 <、<=、> 和 >=,比较两个迭代器的相对位置。
  •     迭代器与整型数值 n 之间的加法和减法操作符 +、+=、- 和 -=,结果是迭代器在容器中向前(或退回)n 个元素。
  •     两个迭代器之间的减法操作符(–),得到两个迭代器间的距离。
  •     下标操作符 iter[n],这是 *(iter + n) 的同义词。

    需要随机访问迭代器的泛型算法包括 sort 算法。vector、deque 和string 迭代器是随机访问迭代器,用作访问内置数组元素的指针也是随机访问迭代器。

 四、迭代器的实现

迭代器的作用就是提供一个遍历容器内部所有元素的接口,因此迭代器的内部必须保存一个与容器相关联的指针,然后重载各种运算操作来方便遍历,其中最重要的就是operator*运算符和operator->运算符,以及++,--等可能需要的运算符重载。

下面参照智能指针实现了一个简单vector的迭代器。vecIter主要作用就是包裹一个指针,不同容器内部数据结构不相同,因此迭代器操作符重载的实现也会不同。比如++操作符,对于线性分配内存的数组来说,直接对指针执行++操作即可,但是如果容器是List就需要采用元素内部的方法,比如ptr->next()之类的方法访问下一个元素。因此,STL容器都实现了自己的专属迭代器。

template<class Item>
class vecIter{
    Item *ptr;
public:
    typedef std::forward_iterator_tag iterator_category;
    typedef Item value_type;
    typedef Item* pointer;
    typedef Item& reference;
    typedef std::ptrdiff_t difference_type;
public:
    vecIter(Item *p = 0) :ptr(p){}
    Item& operator*()const{
        return *ptr;
    }
    Item* operator->()const{
        return ptr;
    }
    //pre
    vecIter& operator++(){
        ++ptr;
        return *this;
    }
    vecIter operator++(int){
        vecIter tmp = *this;
        ++*this;
        return tmp;
    }
 
    bool operator==(const vecIter &iter){
        return ptr == iter.ptr;
    }
    bool operator!=(const vecIter &iter){
        return !(*this == iter);
    }
 
};
int main(){
int a[] = { 1, 2, 3, 4 };
    std::cout << std::accumulate(vecIter<int>(a), vecIter<int>(a + 4), 0);//输出 10
 
}

参考:

迭代器模式(详解版)

C++:标准模板库(STL)_码农code之路的博客-CSDN博客

C++标准模板库(STL)迭代器的原理与实现_wutao02的博客-CSDN博客_stl迭代器实现

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++之STL迭代器 的相关文章

  • 为什么迭代器使用“!=”而不是“<”?

    我习惯这样写循环 for std size t index 0 index lt foo size index Do stuff with foo index 但是当我在其他人的代码中看到迭代器循环时 它们看起来像这样 for Foo It
  • 为什么编译器无法用文字确定 std::max 的模板?

    既不是 clang 也不是 gcc 编译这个 include
  • 在没有缓冲区的情况下将数据从 fstream 复制到 stringstream?

    无论如何 我可以从fstream 一个文件 到一个stringstream 内存中的流 目前 我正在使用缓冲区 但这需要双倍的内存 因为您需要将数据复制到缓冲区 然后将缓冲区复制到字符串流 直到删除缓冲区为止 数据都会在内存中复制 std
  • 在Linux上运行MFC程序

    我有一个相当大的基于 MFC 的程序 我的任务是让它在 Linux 上运行 我已经解释过 这需要将程序重新编写为带有 STL 的直接 C 更多工作 或者重新编写为 Qt C 更少工作 现在我被告知 我需要编写包装器以使每个 MFC 类在 L
  • 重写标准库使用的内存分配方法? [复制]

    这个问题在这里已经有答案了 是否可以覆盖 STL 分配 管理和释放内存的方式 如果可能的话 人们会怎样做呢 有没有一种方法可以将处理原始内存的代码保留在一个类或文件中 我想对我的整个程序执行此操作 以便我可以跟踪内存使用情况 时间和生命周期
  • std::unordered_set 迭代器遍历的复杂性

    我最近玩了一个std unordered set http en cppreference com w cpp container unordered set 我怀疑我的 STL 版本会跟踪某些 FILO 数据结构 看起来像列表 中的非空存
  • 迭代器后继者

    我想用另一个迭代器 同类 的后继者初始化一个迭代器 任意类型 以下代码适用于随机访问迭代器 但不适用于前向或双向迭代器 Iterator i j 1 一个简单的解决方法是 Iterator i j i 但这不起作用初始化语句for 循环的
  • 区分映射和集合的模板

    在创建代码时common for set unordered set map and unordered map 我需要几种方法 其中处理实际上是不同的 我的问题是让编译器推断出要使用哪个实现 考虑这个例子 include
  • 专门化 STL 算法,以便它们在可用时自动调用高效的容器成员函数

    STL 具有全局算法 可以在任意容器上运行 只要它们支持该算法的基本要求 例如 某些算法可能要求容器具有随机访问迭代器 例如向量而不是列表 当容器具有比通用算法更快的执行方式时 它会提供具有相同名称的成员函数来实现相同的目标 就像提供自己的
  • uninitialized_copy memcpy/memmove 优化

    我最近开始研究 MSVC 实现中的 STL 那里有一些不错的技巧 但是我不知道为什么使用以下标准 The std uninitialized copy被优化为一个简单的memcpy memmove如果满足某些条件 据我了解 输入范围可以是m
  • 如何将 list 对象附加到另一个对象

    在 C 中 我有两个list
  • STL迭代器是否保证集合更改后的有效性?

    假设我有某种集合 并且我在它的开头获得了一个迭代器 现在假设我修改了该集合 无论集合或迭代器的类型如何 我仍然可以安全地使用迭代器吗 为了避免混淆 以下是我讨论的操作顺序 获取集合的迭代器 修改集合 显然 不是其中的元素 而是集合本身 使用
  • 如何修复 STL 样式容器以容纳不完整或抽象类型?

    几天前 我尝试以与 STL 容器相同的风格编写一个基本的树实现 现在我尝试在我的代码中使用它 但是有两件事似乎不起作用 但可以说std vector 即 使用不完整类型和使用抽象类型 如何修复我的树实现以获得此功能 我尝试稍微压缩一下我的代
  • const_iterator 和 iterator 有什么区别? [复制]

    这个问题在这里已经有答案了 这两者在 STL 内部的实现方面有什么区别 性能方面有什么区别 我想当我们以 只读方式 遍历向量时 我们更喜欢const iterator right 谢谢 没有性能差异 A const iterator是一个指
  • 如何从 wfstream 读取二进制数据?

    我从文件读取数据时遇到一个小问题 我希望能够读取 wstring 以及任意大小的原始数据块 大小以字节为单位 std wfstream stream file c str std wstring comType stream gt gt c
  • 如何确保 std::map 是有序的?

    Using a std map
  • 为什么 std::set 没有“包含”成员函数?

    我正在大量使用std set
  • 在 boost 元组、zip_iterator 等上使用 std::get 和 std::tie

    我有哪些使用选择std get lt gt and std tie lt gt 与增强结构一起 例子 我想使用基于范围的 for 循环在多个容器上进行迭代 我可以实施zip函数 它使用boost zip iterator include
  • 我应该使用函数还是无状态函子?

    这两段代码做同样的事情 如您所见 它将用于排序函数 哪个更好 我通常写后一种 但我看到一些程序员像以前那样做 struct val lessthan binary function
  • std::类似向量的类经过优化以容纳少量项目[重复]

    这个问题在这里已经有答案了 在程序的一个时间关键部分中 有一个类成员如下所示 std vector m vLinks 在分析过程中 我注意到该向量大约 99 98 的执行仅包含 0 或 1 个项目 然而 在极少数情况下 它可能会容纳更多 根

随机推荐

  • 数组名和函数名是什么东西

    数组名和函数名的本质都是一个 指向数组首地址或函数体的指针常量 的名字 规则1 xff1a 数组 61 指向数组首地址的指针常量 43 数组元素 简单说就是 xff0c 数组 61 指针常量 43 数组内容 xff0c 数组名就是这个指针常
  • RT-Thread进阶之低功耗PM组件应用笔记

    电源管理组件 嵌入式系统低功耗管理的目的在于满足用户对性能需求的前提下 xff0c 尽可能降低系统能耗以延长设备待机时间 高性能与有限的电池能量在嵌入式系统中矛盾最为突出 xff0c 硬件低功耗设计与软件低功耗管理的联合应用成为解决矛盾的有
  • 【STM32H750】玩转ART-Pi(一)——使用STM32CUBMX生成TouchGFX工程

    目录 STM32H750 玩转ART Pi xff08 一 xff09 使用STM32CUBMX生成TouchGFX工程 STM32H750 玩转ART Pi xff08 二 xff09 制作MDK的外部QSPI FLASH烧录算法 STM
  • 【STM32H750】玩转ART-Pi(二)——制作MDK的外部QSPI-FLASH烧录算法

    目录 STM32H750 玩转ART Pi xff08 一 xff09 使用STM32CUBMX生成TouchGFX工程 STM32H750 玩转ART Pi xff08 二 xff09 制作MDK的外部QSPI FLASH烧录算法 STM
  • linux驱动开发篇(四)—— platform平台设备驱动

    linux系列目录 xff1a linux基础篇 xff08 一 xff09 GCC和Makefile编译过程 linux基础篇 xff08 二 xff09 静态和动态链接 ARM裸机篇 xff08 一 xff09 i MX6ULL介绍 A
  • C语言变参函数和可变参数宏

    文章目录 一 变参函数的设计与实现1 变参函数初体验2 变参函数改进版3 变参函数 V3 0 版本3 变参函数 V4 0 版本4 变参函数 V5 0 版本 二 可变参数宏的设计与实现1 什么是可变参数宏2 宏连接符 的作用3 可变参数宏的另
  • 利用MDK的FLM文件生成通用flash驱动

    文章目录 前言一 FLM文件是什么 xff1f 二 FLM文件结构1 FlashPrg c2 FlashPrg c 三 解析FLM文件1 解析flm文件 四 设计flash驱动抽象层五 快速使用 前言 在进行Flash操作时 xff0c 一
  • 游标的概念和作用

    游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制 游标充当指针的作用 尽管游标能遍历结果中的所有行 xff0c 但他一次只指向一行 概括来讲 xff0c SQL的游标是一种临时的数据库对象 xff0c 即可以用来存放在数
  • 【STM32H750】从零编写MDK的FLM烧录算法

    文章目录 前言一 将代码中的图片资源下载到外部flash1 修改分散加载文件 二 MDK下载算法原理1 程序能够通过下载算法下载到芯片的原理2 算法程序中擦除操作执行流程3 制作FLM文件步骤 三 使用STM32CubeMX新建工程1 新建
  • 使用MDK开发树莓派pico RP2040之外部 flash下载算法

    文章目录 前言一 MDK下载算法原理1 程序能够通过下载算法下载到芯片的原理2 算法程序中操作执行流程3 创建MDK下载算法通用流程 二 树莓派pico下载算法制作步骤1 下载树莓派的MDK的程序模板2 修改输出算法文件的名字3 修改编程算
  • 单片机堆栈分配

    Code xff1a 表示程序所占用 FLASH 的大小 xff08 FLASH xff09 RO data xff1a 即 Read Only data xff0c 表示程序定义的常量 xff0c 如 const 类型 xff08 FLA
  • 【STM32F429】通过STM32CubeMX移植TouchGFX

    目录 STM32F429 移植TouchGFX到RT Thread系统 xff08 1 xff09 STM32F429 使用TouchGFX的MVP架构来实现GUI和硬件的双向交互 xff08 2 xff09 STM32F429 RT Th
  • TouchGFX使用MVP架构来实现GUI和硬件的双向交互

    目录 xff1a 实战 xff1a 1 STM32F767移植touchGFX 使用RT Thread系统实现DIY数字仪表 xff08 完成 xff09 2STM32F429移植touchGFX 使用RT Thread系统实现DIY数字仪
  • python 微信自动回复机器人

    python 微信自动回复机器人 导入wxauto https github com cluic wxauto span class token comment python3 span span class token comment c
  • 《python+opencv实践》一、基于颜色的物体追踪(上)

    点击打开链接 本文主要参考国外一大牛博客 xff0c 然后自己修改得来 相关知识点在这里 实现功能 xff1a 追踪红颜色瓶盖 xff0c 并画出瓶盖轮廓和运动轨迹 from collections import deque import
  • 《python+opencv实践》一、基于颜色的物体追踪(下)

    本文对 python 43 opencv实践 一 基于颜色的物体追踪 xff08 上 xff09 做了功能上的强化 xff0c 强化如下 xff1a xff08 1 xff09 加了pts清空 xff0c 即当没有检测到目标时 xff0c
  • Ubuntu16.04 编译出错c++: internal compiler error: Killed (program cc1plus)

    最近在使用github上的一个模拟器 xff0c 需要自己对其中文件进行make编译 但是中间遇到了不知道多少个错误 xff0c 吐血 想了想还是记录一下 xff0c 错误 compiling moc moc qwt plot panner
  • C++创建信号量 CreateSemaphore

    一 定义 Semaphore也是一个线程同步的辅助类 xff0c 可以维护当前访问自身的线程个数 xff0c 并提供了同步机制 使用Semaphore可以控制同时访问资源的线程个数 xff0c 例如 xff0c 实现一个文件允许的并发访问数
  • OpenGL ES之GLSurfaceView学习一:介绍

    原文地址 http 120 132 134 205 cmdn supesite uid 5358 action viewspace itemid 6527 GLSurfaceView是一个视图 xff0c 继承至SurfaceView xf
  • C++之STL迭代器

    一 背景 迭代器 iterator 是一种抽象的设计理念 xff0c 即迭代器模式 xff0c 通过迭代器可以在不了解容器内部原理的情况下遍历容器 除此之外 xff0c STL中迭代器一个最重要的作用就是作为容器 vector queue