细说C++反向迭代器:原理与用法

文章目录

  • 一、引言
  • 二、反向迭代器的原理与实现细节
  • 三、模拟实现C++反向迭代器
      • 反向迭代器模板类的设计
      • 反向迭代器的使用示例与测试

一、引言

  1. 迭代器与反向迭代器的概念引入

迭代器(Iterator)是C++标准模板库(STL)中的一个核心概念,它提供了一种访问容器中元素的方式,而无需了解容器底层的实现细节。迭代器就像是一个指向容器中元素的指针,通过它可以遍历容器中的元素,进行读取、修改或删除操作。

反向迭代器(Reverse Iterator)则是迭代器的一个变种,它允许我们从后向前遍历容器中的元素。反向迭代器的出现极大地丰富了C++中容器的遍历方式,特别是在需要逆向操作容器元素时,提供了极大的便利。

  1. 反向迭代器在C++中的重要作用

反向迭代器在C++中扮演着至关重要的角色。在处理一些需要逆向遍历容器的场景时,如从后向前打印数组元素、逆序遍历链表节点等,使用反向迭代器可以大大简化代码逻辑,提高代码的可读性和可维护性。此外,反向迭代器还使得一些复杂的算法实现变得更加简单和直观。

本文将详细介绍C++中反向迭代器的概念、原理和使用方法,并通过模拟实现一个简单的反向迭代器来加深读者对反向迭代器的理解。通过本文的学习,读者将能够掌握反向迭代器的基本用法,并能够在实际编程中灵活运用反向迭代器来处理各种需要逆向遍历容器的场景。


二、反向迭代器的原理与实现细节

  1. 反向迭代器的内部机制

反向迭代器是一种特殊的迭代器,它的内部机制基于容器的正向迭代器。通常,反向迭代器在内部持有一个指向容器末尾之后位置的迭代器,或者一个指向容器第一个元素之前的迭代器,这取决于容器的具体实现。当对反向迭代器进行自增或自减操作时,它实际上是在对内部的正向迭代器进行相反的操作,从而实现从后向前的遍历。因此我们在模拟实现反向迭代器时,通常以原容器的正向迭代器为成员,所谓 reverse_iterator ,可以将迭代器的行进方向逆转,使原本应该前进的 operator++ 变成了后退操作, operator--变成了前进操作。

  1. 反向迭代器的设计与实现要点

设计并实现一个反向迭代器需要考虑以下几个要点:

  • 迭代器类型的选择:反向迭代器需要基于某种正向迭代器进行实现。因此,首先需要确定所针对的容器类型及其对应的正向迭代器类型。

  • 解引用与箭头操作符的重载:反向迭代器需要重载解引用操作符(*)和箭头操作符(->),以便能够正确地访问容器中的元素。这通常涉及对内部正向迭代器的相应操作,以确保访问的是正确的元素。

  • 反向遍历的实现:反向迭代器的核心功能是从后向前遍历容器。这通常通过调整正向迭代器的位置来实现。例如,在每次自增操作中,反向迭代器实际上会使内部的正向迭代器向前移动一个位置,从而模拟出从后向前的遍历效果。为了配合迭代器区间的“前闭后开”,我们通常按下图方式设计反向迭代器:

    在这里插入图片描述

  1. 反向迭代器与STL容器的结合使用

在C++标准模板库中,许多容器都提供了反向迭代器的支持。例如,vectorliststring等容器都提供了rbegin()rend()成员函数,用于获取指向容器末尾和末尾之后位置的反向迭代器。通过这些反向迭代器,我们可以方便地实现从后向前的遍历操作。

在实际使用中,我们可以将反向迭代器与范围基于的for循环(C++11及以后版本)或传统的while循环结合使用,来处理需要逆向遍历容器的场景。反向迭代器的使用方式与正向迭代器类似,只是遍历的方向相反而已。


三、模拟实现C++反向迭代器

在C++中,反向迭代器是一种特殊的迭代器,它允许我们按照相反的顺序遍历容器中的元素。在本节中,我们将模拟实现一个通用的反向迭代器模板类,并详细解释其设计、实现以及使用示例。

反向迭代器模板类的设计

为了设计一个通用的反向迭代器模板类,我们需要考虑以下几个关键部分:

  1. 模板参数的选择与意义

    ReverseIterator类的模板参数中,IteratorRefPtr的选择和它们各自的意义如下:

    • IteratorIterator是一个模板参数,它代表了正向迭代器的类型。这个类型通常是一个STL迭代器或者类似的自定义迭代器,用于遍历容器(如vectorlist等)。在ReverseIterator中,Iterator类型用于内部存储,并且在进行反向遍历的时候,它将被用来模拟反向迭代的行为。

    • Ref是另一个模板参数,用于指定operator*的返回类型。它应该是一个引用类型,这样operator*才能返回当前反向迭代器指向的元素的引用。返回引用允许用户直接修改通过反向迭代器访问的元素的值。

    在大多数情况下,Ref可以简单地设置为Iteratorvalue_type&,其中value_type是正向迭代器所指向元素的类型。例如,如果Iteratorvector<int>::iterator,那么Ref就应该是int&

    • Ptr是第三个模板参数,用于指定operator->的返回类型。它应该是一个指针类型,这样operator->才能返回当前反向迭代器指向的元素的指针。这个指针类型通常用于通过->操作符访问元素的成员。

    同样地,Ptr可以设置为Iteratorvalue_type*。对于上面的vector<int>::iterator示例,Ptr就是int*

    template<class Iterator, class Ref, class Ptr>
    class ReverseIterator {
    public:typedef ReverseIterator<Iterator, Ref, Ptr> Self;//...
    }
    
  2. 成员变量与构造函数的实现

    • 成员变量_it:存储正向迭代器的实例,用于实现反向遍历。 Iterator _it; // 存储正向迭代器

    • 构造函数:接受一个正向迭代器作为参数,初始化成员变量_it ReverseIterator(Iterator it) : _it(it) {}

  3. 反向迭代器核心操作符的重载

    • 自增与自减操作符operator++operator--。反向迭代器的自增操作应模拟反向遍历,因此operator++应使内部的正向迭代器向前移动一位,而operator--则应使正向迭代器向后移动一位。
    Self& operator++() {--_it; // 使正向迭代器向前移动一位,模拟反向迭代器的自增return *this;
    }Self& operator--() {++_it; // 使正向迭代器向后移动一位,模拟反向迭代器的自减return *this;
    }
    
    • 解引用与箭头操作符operator*operator->。这些操作符应返回当前反向迭代器指向的元素的引用或指针。
    Ref operator*() const {  Iterator cur = _it;  return *--cur; // 返回当前反向迭代器指向的元素的引用  
    }  Ptr operator->() const {  return &(this->operator*()); // 返回当前反向迭代器指向的元素的指针  
    }
    

    operator*成员函数用于获取反向迭代器当前指向的元素的引用。它首先创建一个_it的副本cur,以避免修改原始的_it。然后,它递减cur以模拟反向迭代器的行为(因为_it实际上是正向迭代器)。递减后,cur指向了当前反向迭代器所代表的元素,并返回这个元素的引用。

    operator->成员函数用于获取当前反向迭代器指向的元素的指针。它通过调用operator*来获取元素的引用,并取这个引用的地址来得到指针。这允许我们像使用普通指针一样,通过->操作符来访问对象的成员。

    例如,如果_it指向vector的末尾(end()),那么cur递减后就会指向最后一个元素。每次递增反向迭代器时,_it实际上是递减的,所以每次调用operator*时,我们都需要递减cur来获取正确的元素。operator->成员函数用于获取当前反向迭代器指向的元素的指针。它通过调用operator*来获取元素的引用,并取这个引用的地址来得到指针。这允许我们像使用普通指针一样,通过->操作符来访问对象的成员。

    需要注意的是,这种实现假设Iterator(即正向迭代器)支持operator*operator->,并且返回的类型与RefPtr兼容。在标准库中的迭代器类型中,这通常是成立的。但对于自定义迭代器类型,需要确保这些操作符的行为符合预期。

    • 比较操作符operator!=operator==。用于比较两个反向迭代器是否指向相同的位置。
    bool operator!=(const Self& s) const { return _it != s._it; }
    bool operator==(const Self& s) const { return _it == s._it; }
    

反向迭代器的使用示例与测试

为了测试我们实现的反向迭代器,我们可以使用一个简单的整数数组作为示例,并创建一个基于该数组的反向迭代器:

#include <iostream>
#include <iterator>
#include <vector>using namespace std;
int main() {std::vector<int> vec = { 1, 2, 3, 4, 5 };ReverseIterator<vector<int>::iterator,int&,int*> rbegin(vec.end());ReverseIterator<vector<int>::iterator, int&, int*> rend(vec.begin());// 使用基于范围的for循环遍历反向迭代器for (ReverseIterator<vector<int>::iterator, int&, int*> it = rbegin; it != rend; ++it) {cout << *it << " "; // 输出:5 4 3 2 1 }cout << endl;ReverseIterator<vector<int>::iterator, int&, int*> it = rbegin;// 测试自增和自减操作符it++; // 现在it指向4cout << *it << endl; // 输出:4--it; // 现在it又指向5cout << *it << endl; // 输出:5// 测试比较操作符if (it != rend) cout << "it is not equal to rend" << endl;return 0;
}

这部分代码整体不难理解,不再赘述。

反向迭代器,以及模拟实现stringvectorlist的反向迭代器的代码详细实现:Project1_list · 比奇堡的Zyb/每日学习 - 码云 - 开源中国 (gitee.com)

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

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

相关文章

matplotlib-直方图

日期&#xff1a;2024.03.114 内容&#xff1a;将matplotlib的常用方法做一个记录&#xff0c;方便后续查找。 # 引入需要的库 from matplotlib import pyplot as plt import numpy as np# 设置画布大小 plt.figure(figsize (20,8),dpi 200)# 全局设置中文字体 plt.rcParams…

Android NDK入门:在应用中加入C和C++的力量

目录 ​编辑 引 NDK的设计目的 与Java/Kotlin的结合 使用场景 开发流程 设置项目以支持NDK 编写本地代码 使用JNI连接本地代码和Java/Kotlin代码 编译和运行你的应用 附 引 自诩方向是android方向的移动端开发工程师&#xff0c;却从来没有真正仔细了解过NDK&#…

Swift:.ignoresSafeArea():自由布局的全方位掌握

ignoresSafeArea(_ regions : edges:)修饰符的说明 SwiftUI布局系统会调整视图的尺寸和位置&#xff0c;以避免特定的安全区域。这就确保了系统内容&#xff08;比如软件键盘&#xff09;或设备边缘不会遮挡您的视图。要将您的内容扩展到这些区域&#xff0c;您可以通过应用该修…

植物神经功能紊乱患者每天从5片黛力新减少至2片,只因找对了治疗方法!

植物神经功能紊乱是一种常见的心理疾病&#xff0c;其症状包括焦虑、失眠、疲劳、头痛、胃肠不适等&#xff0c;给患者带来很大的困扰。然而&#xff0c;这种疾病是可以治疗的。本文将介绍一位植物神经功能紊乱患者的治疗经历&#xff0c;希望能够帮助更多的人了解和治疗此病。…

生成二维码及加入logo和文字

<html> <!-- 存放二维码的容器 --> <div idqrcode></div> <script typetext/javascript srchttp://cdn.staticfile.org/jquery/2.1.1/jquery.min.js></script> <script src"https://cdn.bootcss.com/jquery.qrcode/1.0/jquery.qr…

Three.js基础入门介绍——Three.js学习七【播放模型动画时模型沿着轨迹移动】

效果描述 在播放导入的模型动画同时&#xff0c;让模型沿着预定路径轨迹移动。例如导入一个会跑步动作的模型&#xff0c;让它沿着一条类似跑道的路径跑步移动。 实现流程 基本流程 1、搭建场景 2、添加模型和播放动画 3、添加路径和模型移动 工程文件 工程文件结构如下图&…

谁用过腾讯云轻量应用服务器2核2G3M配置,支持多少人在线?

腾讯云轻量应用服务器2核4G5M配置一年优惠价165元、252元15个月、三年756元&#xff0c;100%CPU性能&#xff0c;5M带宽下载速度640KB/秒&#xff0c;60GB SSD系统盘&#xff0c;月流量500GB&#xff0c;折合每天16.6GB流量&#xff0c;超出月流量包的流量按照0.8元每GB的价格支…

常用芯片学习——TP4057电源管理芯片

TP40578 500mA线性锂离子电池充电器 芯片介绍 TP4057是一款性能优异的单节锂离子电池恒流/恒压线性充电器。TP4057采用S0T23-6封装配合较少的外围原件使其非常适用于便携式产品&#xff0c;并且适合给USB电源以及适配器电源供电。 基于特殊的内部MOSFET架构以及防倒充电路&a…

Python实现一笔画游戏

Python实现一笔画游戏 关于一笔画介绍可参见“HTML5实现一笔画游戏”https://blog.csdn.net/cnds123/article/details/136669088 在Python中&#xff0c;Tkinter是一个广泛使用的标准GUI库&#xff0c;我们将使用它来实现这个游戏。 先给出效果图&#xff1a; 连接线段时&am…

城乡居民基本医疗信息管理系统|基于Springboot的城乡居民基本医疗信息管理系统设计与实现(源码+数据库+文档)

城乡居民基本医疗信息管理系统目录 目录 基于Springboot的城乡居民基本医疗信息管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、病例管理 2、医院资讯信息管理 3、医院资讯类型管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选…

微信小程序开发学习笔记《21》uni-app框架-楼层图片跳转

微信小程序开发学习笔记《21》uni-app框架-楼层图片跳转 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。建议仔细阅读uni-app对应官方文档 一、创建新的分包goods_list 二、将请求到的楼层数据url调整为本地的 可以看到上图是请求…

关于固件的简单解释

我不喜欢等人也不喜欢被别人等——赤砂之蝎 简而言之 固件是什么 固件&#xff08;Firmware&#xff09;是一种软件类型&#xff0c;它是嵌入式系统中的一部分&#xff0c;通常存储在设备的非易失性存储器中&#xff0c;如闪存或ROM&#xff08;只读存储器&#xff09;。与操作…

libevent中bufferevent事件及常用的API函数

自带buffer的事件-bufferevent bufferevent实际上也是一个event&#xff0c;只不过比普通的event高级&#xff0c;他的内部有两个缓冲区&#xff0c;以及一个文件描述符&#xff08;网络套接字&#xff09;。一个网络套接字有读写两个缓冲区&#xff0c;bufferevent同样也带有…

探索仿函数(Functor):C++中的灵活函数对象

文章目录 一、仿函数定义及使用二、仿函数与函数指针的区别三、仿函数与算法的关系四、仿函数的实践用例 在C编程中&#xff0c;我们经常需要对数据进行排序、筛选或者其他操作。为了实现这些功能&#xff0c;C标准库提供了许多通用的算法和容器&#xff0c;而其中一个重要的概…

nut-ui组件库icon中使用阿里图标

1.需求 基本每个移动端组件库都有组件 icon组件 图标组件、 但是很多组件库中并找不到我们需要的图标 这时候 大家有可能会找图标库 最大众的就是iconfont的图标了 2.使用 有很多方式去使用这个东西 比如将再限链接中的css引入 在使用 直接下载图标 symbol 方式 等....…

【NR 定位】3GPP NR Positioning 5G定位标准解读(十三)-DL-AoD定位

前言 3GPP NR Positioning 5G定位标准&#xff1a;3GPP TS 38.305 V18 3GPP 标准网址&#xff1a;Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;一&#xff09;-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;…

buuctf warmup 超详细

目录 1.代码审计&#xff1a; 2.逻辑分析 3.总结分析 4.分析记录 5.疑点解答 1.代码审计&#xff1a; <?phphighlight_file(__FILE__);class emmm //定义了一个类{public static function checkFile(&$page) 类里面又申明创建…

Android 架构师研发技术进阶之路:不同阶段需要掌握的那些技术及软技能

资深 而到了资深层次&#xff0c;技术栈已经不再是阻碍。能够从更高层面看待问题&#xff0c;理解整个系统的设计&#xff0c;作为系统架构师的角色存在。 1. 理解微服务、SOA思想&#xff0c;对于后端开发有一定涉猎。 2. 了解前端研发工具和思想&#xff0c;知道vue react…

centos破解root密码以及如何防止他人破解root密码

目录 破解root密码 服务器重启 1.再重启页面上下选择第一个按e进入内核编辑模式 2.找到linux16开头的一行&#xff0c;光标移动到最后添加 init/bin/sh Ctrlx 保存 3.进入单用户模式 4.重新挂在根分区 5.关闭selinux 6.更新密码 passwd 7.在根分区下面创建一个隐藏文件…

【C语言步行梯】一维数组、二维数组介绍与应用详谈

&#x1f3af;每日努力一点点&#xff0c;技术进步看得见 &#x1f3e0;专栏介绍&#xff1a;【C语言步行梯】专栏用于介绍C语言相关内容&#xff0c;每篇文章将通过图片代码片段网络相关题目的方式编写&#xff0c;欢迎订阅~~ 文章目录 为什么要有数组&#xff1f;一维数组数组…