【C++】反向迭代器

一、前言

在前面对vector等容器的学习中,我们学会了如何去使用正向迭代器并模拟实现

但是我们没有去模拟实现反向迭代器,这篇文章中我们就来了解反向迭代器的底层并实现它,把之前的坑给填上。


二、反向迭代器

反向迭代器的底层设计十分精妙,当你真正了解了它的实现方式,一定会拍案叫绝

我们先以list类为例,来实现它的反向迭代器。

list类的正向迭代器中,begin和end的位置如下:

之前我们实现的list类的正向迭代器是这样的:

	template<class T, class Ref, class Ptr>struct __list_iterator {typedef list_node<T> node;typedef __list_iterator<T, Ref, Ptr> self;node* _node;__list_iterator(node* n):_node(n){}Ref& operator*(){ return _node->_data;}Ptr operator->(){return &_node->_data;}self& operator++(){_node = _node->_next;return *this;}self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}self& operator--(){_node = _node->_prev;return *this;}self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};

2.1 修改正向迭代器 

有些人就认为,反向迭代器直接把正向迭代器修改一下不就可以了吗?把向前走改成向后走,向后走改成向前,例如:

	template<class T, class Ref, class Ptr>struct __list_reverse_iterator{typedef list_node<T> node;typedef __list_reverse_iterator<T, Ref, Ptr> self;node* _node;__list_reverse_iterator(node* n):_node(n){}Ref& operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}self& operator++(){_node = _node->_prev;return *this;}self operator++(int){self tmp(*this);_node = _node->_prev;return tmp;}self& operator--(){_node = _node->_next;return *this;}self operator--(int){self tmp(*this);_node = _node->_next;return tmp;}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};

这种方式实现的反向迭代器, rbegin和rend的位置如下:

用正向迭代器修改得到的反向迭代器,也能正常的完成我们需要的功能。

但是这种方式的代码复用率太低,并不是一个好的方法。

2.2 封装正向迭代器

我们可以看看标准库中的反向迭代器是如何实现的

reverse_iterator - C++ Reference (cplusplus.com)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/iterator/reverse_iterator/具体是如何实现的呢?我们直接放出模拟实现的代码:

template<class Iterator, class Ref, class Ptr>
struct __list_reverse_iterator
{typedef __list_reverse_iterator<Iterator, Ref, Ptr> self;Iterator _cur;__list_reverse_iterator(Iterator it):_cur(it){}Ref operator*(){Iterator tmp = _cur;--tmp;return *tmp;}self& operator++(){--_cur;return *this;}self& operator--(){++_cur;return *this;}bool operator!=(const self& s){return _cur != s._cur;}bool operator==(const self& s){return _cur == s._cur;}
};template<class T>
class list
{typedef list_node<T> node;
public:typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;typedef __list_reverse_iterator<iterator, T&, T*> reverse_iterator;typedef __list_reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;//...reverse_iterator rbegin(){return reverse_iterator(end()); //end和begin都会返回一个正向迭代器,用这个正向迭代器来构造反向迭代器}const_reverse_iterator rbegin() const{return const_reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}//...
}

简单来说,标准库中的反向迭代器,实际上是一个对正向迭代器的封装

当--反向迭代器时,实际上就是对内部封装的正向迭代器进行++操作;

而++反向迭代器时,实际上就是对内部封装的正向迭代器进行--操作。

通过模拟实现的代码,我们会发现: 

在对反向迭代器解引用时,实际上解引用的是其前面的位置

例如,假设反向迭代器此时在哨兵位,对其解引用时,实际上解引用的是5的位置

所以反向迭代器实际上也是一个适配器,通过提供特定的函数对内部封装的正向迭代器进行操作,来实现反向迭代器的功能。

这种实现反向迭代器的方式,rbegin和rend的位置如下:

这样就刚好和正向迭代器的begin和end对齐

进行测试,可以正常的实现功能

为什么我们选择用封装正向迭代器的方式来实现反向迭代器呢?这种方式感觉反而更麻烦了

实际上,其最大的优点在于,用适配器的形式来实现反向迭代器,就可以适用于任何一个容器了

当然前提是这个容器需要有双向迭代器,毕竟需要用到++和--。

例如我们把上面写的反向迭代器放到一个头文件中,并换成vector来使用它

我们知道,相比list的正向迭代器是对指针的封装,vector的正向迭代器是一个原生态指针

但是因为反向迭代器是一个适配器,所以不需要关心正向迭代器的底层如何,只需要能够符合需求,能够完成对应的操作,就可以封装进反向迭代器

例如:

到这里,我们知道反向迭代器的底层实际上就是一个正向迭代器,对其进行了封装来实现反向迭代器的功能,通过这种实现反向迭代器的方式就可以适用于所有支持双向迭代器和随机迭代器的容器

如果有错误的地方,欢迎在评论区指出

完.

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

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

相关文章

python统计分析——灵敏度、特异度和ROC曲线

参考资料&#xff1a;python统计分析【托马斯】 1、灵敏度和特异度 灵敏度&#xff1a;也叫作效能。被检验正确识别出来的阳性结果&#xff08;病人中有疾病且检验结果是阳性的概率&#xff09;。 特异度&#xff1a;被检验正确识别出来的阴性结果&#xff08;病人健康且检验结…

大模型融合方法-DARE

LLM在SFT之后会产生大量的冗余参数(delta参数)&#xff0c;阿里团队提出DARE方法来消除delta参数&#xff0c;并将其合并到PRE模型中&#xff0c;从而实现多源模型能力的吸收。 DARE无需GPU重新训练&#xff0c;其思路非常简单&#xff0c;就跟dropout类似&#xff1a; m t ∼…

针对pycharm打开新项目需要重新下载tensorflow的问题解决

目录 一、前提 二、原因 三、解决办法 一、前提 下载包之前&#xff0c;已经打开了&#xff0c;某个项目。 比如&#xff1a;我先打开了下面这个项目&#xff1a; 然后在terminal使用pip命令下载&#xff1a; 如果是这种情况&#xff0c;你下载的这个包一般都只能用在这一个…

自动驾驶轨迹规划之时空语义走廊(一)

欢迎大家关注我的B站&#xff1a; 偷吃薯片的Zheng同学的个人空间-偷吃薯片的Zheng同学个人主页-哔哩哔哩视频 (bilibili.com) 目录 1.摘要 2.系统架构 3.MPDM 4.时空语义走廊 ​4.1 种子生成 4.2 具有语义边界的cube inflation ​4.3 立方体松弛 本文解析了丁文超老师…

如何计算KST指标,昂首资本一个公式计算

在上一篇文章中&#xff0c;Anzo Capital昂首资本和各位投资者一起了解了KST指标&#xff0c;今天我们继续分享如何计算KST指标。 首先投资者可以在时间范围9、12、18和24分析变化率值。 前三个值(时间帧9、12、18)用EMA 26平滑&#xff0c;最后一个值用EMA 39平滑。 然后&…

从0开始搭建基于VUE的前端项目

准备与版本 安装nodejs(v20.11.1)安装vue脚手架(vue/cli 5.0.8) ,参考&#xff08;https://cli.vuejs.org/zh/&#xff09;vue版本&#xff08;2.7.16&#xff09;&#xff0c;vue2的最后一个版本 初始化项目 创建一个git项目&#xff08;可以去gitee/github上创建&#xff…

数据分析之POWER Piovt透视表分析

将几个数据表之间进行关联 生成数据透视表 超级透视表这里的字段包含子字段 这三个月份在前面的解决办法 1.选中这三个月份&#xff0c;鼠标可移动的时候移动到后面 2.在原数据进行修改 添加列获取月份&#xff0c;借助month的函数双击日期 选择月份这列----按列排序-----选择月…

搜索与图论——Dijkstra算法求最短路

最短路算法 稠密图与稀疏图 n为点数&#xff0c;m为边数。m远小于n的平方为稀疏图&#xff0c;m接近n的平方为稠密图。 稀疏图用邻接表存&#xff0c;稠密图用邻接矩阵存 朴素版dijkstra时间复杂度为O(n^2),对于稠密图可以ac&#xff0c;但遇到稀疏图时会TLE。 dijkstra函数实…

Linux权限管理

文章目录 linux权限管理1.Linux权限的概念2.Linux权限管理2.1 文件访问者的分类&#xff08;人&#xff09;2.2 文件类型和访问权限&#xff08;事物属性&#xff09;2.2.1 文件类型2.2.2 基本权限 2.3文件权限值的表示方法2.3.1 字符表示方法2.3.2 八进制数值表示方法 2.4 文件…

CV领域 交叉注意力(Cross Attention)中QKV的含义理解

交叉注意力公式&#xff1a; 注意力的输入&#xff1a; &#xff08;1&#xff09;KV&#xff1a;图像的全局特征 &#xff08;2&#xff09;Q&#xff1a;告诉attention需要关注哪些重要特征 公式计算过程理解&#xff1a; &#xff08;1&#xff09;&#xff1a;Q和K相乘…

后疫情时代CS保研沉思录暨2023年个人保研经验贴

个人情况 正如古话所说&#xff0c;最适合你的才是最好的。因此这里先贴上个人基本情况&#xff0c;用作参考。 如果你的个人情况与我相近&#xff0c;则有更强的参考作用。如果情况相差较大&#xff0c;也可以姑且引为例子来研究。 学校层次&#xff1a;中流至末流211 专业…

C之易错注意点转义字符,sizeof,scanf,printf

目录 前言 一&#xff1a;转义字符 1.转义字符顾名思义就是转换原来意思的字符 2.常见的转义字符 1.特殊\b 2. 特殊\ddd和\xdd 3.转义字符常错点----计算字符串长度 注意 &#xff1a; 如果出现\890,\921这些的不是属于\ddd类型的&#xff0c;&#xff0c;不是一个字符…

把本地文件上传到HDFS上操作步骤

因为条件有限&#xff0c;我这里以虚拟机centos为例 实验条件&#xff1a;我在虚拟机上创建了三台节点&#xff0c;部署了hadoop&#xff0c;把笔记本上的数据上传到hdfs中 数据打包上传到虚拟机节点上 采用的是rz命令&#xff0c;可以帮我们上传数据 没有的话可以使用命令安装…

那个男人开发了一个多账号浏览器

核心价值1&#xff1a;实现了一个浏览器同时登录多个账号&#xff0c;标签页之间信息不共享核心价值2:聚焦于账号管理&#xff0c;所有需要登录多账号都用这个浏览器减轻了找入口的烦恼核心价值3:赏心悦目核心价值4:悬浮功能核心价值5:PC和手机模式一键切换核心价值6:不同于浏览…

Redis 全景图(1)--- 关于 Redis 的6大模块

这是我第一次尝试以长文的形式写一篇Redis的总结文章。这篇文章我想写很久了&#xff0c;只是一直碍于我对Redis的掌握没有那么的好&#xff0c;因此迟迟未动笔。这几天&#xff0c;我一直在看各种不同类型的Redis文章&#xff0c;通过阅读这些文章&#xff0c;引发了我对于Red…

DolphinScheduler on k8s 云原生部署实践

文章目录 前言利用Kubernetes技术云原生平台初始化迁移基于Argo CD添加GitOpsDolphinScheduler 在 k8s 上的服务自愈可观测性集成服务网格云原生工作流调度从HDFS升级到S3文件技术总结 前言 DolphinScheduler 的高效云原生部署模式&#xff0c;比原始部署模式节省了95%以上的人…

【论文通读】AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation

AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation 前言AbstractMotivationFrameworkConversable AgentsConversation Programming ApplicationA1: Math Problem SolvingA2: Retrieval-Augmented Code Generation and Question AnsweringA3: Decision…

相机标定学习记录

相机标定是计算机视觉和机器视觉领域中的一项基本技术&#xff0c;它的主要目的是通过获取相机的内部参数&#xff08;内参&#xff09;和外部参数&#xff08;外参&#xff09;&#xff0c;以及镜头畸变参数&#xff0c;建立起现实世界中的点与相机成像平面上对应像素点之间准…

深度学习算法概念介绍

前言 深度学习算法是一类基于人工神经网络的机器学习方法&#xff0c;其核心思想是通过多层次的非线性变换&#xff0c;从数据中学习表示层次特征&#xff0c;从而实现对复杂模式的建模和学习。深度学习算法在图像识别、语音识别、自然语言处理等领域取得了巨大的成功&#xf…

【STM32 HAL库SPI/QSPI协议学习,基于外部Flash读取。】

1、SPI协议 简介 SPI 协议是由摩托罗拉公司提出的通讯协议 (Serial Peripheral Interface)&#xff0c;即串行外围设备接口&#xff0c;是 一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间&#xff0c;要求通讯速率 较高的场合。 SPI 物理层 SPI 通讯…