【C++】反向迭代器的实现

news2024/11/24 16:53:08

文章目录

  • 1.迭代器的分类
  • 2.反向迭代器的使用
  • 3.反向迭代器的模拟实现
  • 4.list类的反向迭代器实现

1.迭代器的分类

我们随便打开一个容器,看迭代器相关的接口,都可以发现,支持迭代器的容器,其迭代器有以下几类

  • 正向迭代器
  • const正向迭代器
  • 反向迭代器
  • const反向迭代器

image-20230428145547611

2.反向迭代器的使用

对于正向迭代器,我么已经很熟悉了,在之前的STL容器的模拟实现中都已经实现过了。那么现在来看看反向迭代器

❗❗反向迭代器的特点

✅反向迭代器的迭代顺序与正向迭代器相反

✅rbegin()相当于end()

✅rend()相当于begin()

✅反向迭代器++相当于正向迭代器–

那么现在,让我们来看一看反向迭代器的使用与正向迭代器的区别

void Test1()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	auto rit = v.rbegin();
	while (rit != v.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}

image-20230428153340421

可以看到,反向迭代器的使用和正向迭代器没有任何区别,只是迭代的顺序不同。

3.反向迭代器的模拟实现

对于反向迭代器的实现,我们去参考一下库里面是怎么实现的

注:这里参照的库是SGI的3.0版本,与侯捷老师的STL源码剖析相对应

image-20230428153855446

这里以list的反向迭代器实现为例,可以看到库里面对反向迭代器的实现是通过实例化reverse_iterator类来实现的,通过用法可以推测出来reverse_iterator类模板的实现逻辑是通过传入的正向迭代器的类型来实例化出不同类的反向迭代器,这里反向迭代器就可以理解成一个容器适配器

下面来看一下reverse_iterator类的实现,这个类在头文件stl_iterator中

image-20230428155332091

通过观察库里面的代码,对模拟实现反向迭代器已经大概有了了解,但是其中出现了新语法:萃取,笔者正在学习的路上,目前的能力对萃取的理解有难度,所以本次模拟实现就不使用萃取的方式,而是使用和list迭代器同样的解决方案——传模板参数的方式解决【C++】list的模拟实现

所以,我们可以得到反向迭代器类的框架如下

template<class Iterator, class Ref, class Ptr>//这里对参数的设计参考之前list迭代器的这几
class reverse_iterator
{
    typedef reserve_iterator<Iterator, Ref, Ptr> Self;//这里类型实例化出来太长了,做一个重命名
public:
	//......	
private:
    Iterator _it;
};

成员函数的实现

参照list迭代器类的成员函数,我们需要设计的成员函数有:

  • 解引用
  • 前置后置的++–
  • ->运算符
  • != 和==

按照我们的理解,很轻松能够写出来以下的成员函数。

reverse_iterator(Iterator it)//构造函数
    :_it(it)
    {}
Self& operator++()//前置++
{
    --_it;
    return *this;
}
Self operator++(int)//后置++
{
    Self tmp = _it;
    --_it;
    return tmp;
}
Self& operator--()//前置--
{
    ++_it;
    return *this;
}
Self operator--(int)//后置--
{
    Self tmp = _it;
    ++_it;
    return tmp;
}
Ref operator*()
{
    return *this;
}
Ptr operator->()
{
    return &(operator*());
}
bool operator!=(const Self& s) const
{
    return _it != s._it;
}
bool operator==(const Self& s) const
{
    return _it == s._it;
}

接下来和库里面的对比以下吧

image-20230428161535430

诶?这和我们实现的不一样啊,为啥嘞?“源码之下了无秘密”,我们去看一下list中对迭代器接口的实现

image-20230428161800027

哦哦懂了,原来是这样,在list中对rbegin的实现是使用end()构造的,end返回的是最后一个元素的下一个位置

image-20230428162046763

如果我们要拿到最后一个元素,会调用rbegin()接口,此时如果要拿到最后一个元素,当然要让rbegin先–,指向最后一个元素之后再解引用。

所以最终对于*得到了以下实现,其余不用改变

当然,如果你愿意更改所有容器对反向迭代器的传参,也可以按照上文中的实现方式实现。

Ref operator*()
{
    Iterator tmp = _it;
    return *(--tmp);
}

最后,我们对返现迭代器类的实现完整代码如下

namespace zht
{
	template<class Iterator, class Ref, class Ptr>//这里对参数的设计参考之前list迭代器的这几
	class reverse_iterator
	{
		typedef reserve_iterator<Iterator, Ref, Ptr> Self;//这里类型实例化出来太长了,做一个重命名
	public:
		reverse_iterator(Iterator it)//构造函数
			:_it(it)
		{}
		Self& operator++()//前置++
		{
			--_it;
			return *this;
		}
		Self operator++(int)//后置++
		{
			Self tmp = _it;
			--_it;
			return tmp;
		}
		Self& operator--()//前置--
		{
			++_it;
			return *this;
		}
		Self operator--(int)//后置--
		{
			Self tmp = _it;
			++_it;
			return tmp;
		}
		Ref operator*()
		{
			Iterator tmp = _it;
			return *(--tmp);
		}
		Ptr operator->()
		{
			return &(operator*());
		}
		bool operator!=(const Self& s) const
		{
			return _it != s._it;
		}
		bool operator==(const Self& s) const
		{
			return _it == s._it;
		}
	private:
		Iterator _it;
	};
}

4.list类的反向迭代器实现

有了上述迭代器类的实现,我们现在可以顺便把之前模拟实现的list类中的反向迭代器加上了

typedef zht::reverse_iterator<iterator, T&, T*> reverse_iterator;//反向迭代器
typedef zht::reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;//const反向迭代器
reverse_iterator rbegin()
{
    return reverse_iterator(end());
}
reverse_iterator rend()
{
    return reverse_iterator(begin());
}
const_reverse_iterator rbegin() const
{
    return const_reverse_iterator(end());
}
const_reverse_iterator rend() const
{
    return const_reverse_iterator(begin());
}

image-20230428165042111

至此,就把STL的迭代器完整实现了。

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

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

相关文章

软件测试必备的Linux知识(一)

1. Linux 概述 1.1 测试人员为什么学习linux 对于软件测试人员来说&#xff0c;我们测试的任何产品都是基于操作系统。比如我们每天都在使用的QQ软件&#xff0c;它有windows、ios、Android、Mac OS等版本&#xff0c;需要把QQ安装在各个平台上&#xff0c;才能进行相应的测试…

03 KVM虚拟机镜像制作

文章目录 03 KVM虚拟机镜像制作3.1 概述3.2 制作镜像3.2.1 使用root用户安装qemu-img软件包3.2.2 使用qemu-img工具的创建镜像文件 3.3 修改镜像磁盘空间大小3.3.1 查询当前虚拟机镜像磁盘空间大小3.3.2 修改镜像磁盘空间大小3.3.3 查询修改后的镜像磁盘空间大小 03 KVM虚拟机镜…

WPS作图常见问题+LATLEX

【LaTex】LaTex的下载与安装&#xff08;超详细、超简洁&#xff09; 表格 1、打开WPS表格&#xff0c;切换至“开始”选项卡&#xff0c;单击“绘图边框”按钮&#xff0c;如下图。 2、鼠标变成如下图一样的笔后&#xff0c;按照斜线表头的方向拉动鼠标&#xff0c;然后就给…

【2023程序员必看】前端行业分析

“前端已死&#xff1f;”|“情绪焦虑&#xff1f;” 最近经常在知乎、脉脉等平台上看到有人在渲染前端就业危机&#xff0c;甚至使用“前端已死”的字眼&#xff0c;颇有“语不惊人死不休”的意味。 “前端已死”更多的是一种焦虑情绪的表达。现阶段的市场行情确实不太好&am…

【LLM】离线部署ChatGLM-6B模型

目录 前言 准备环境 打包环境 下载/上传模型 部署模型 前言 甲方出手&#xff0c;天下我有&#x1f929;。圆梦了圆梦了~一直想整一台GPU服务器尝尝鲜&#xff0c;奈何钱包空空&#xff0c;虽然有可以在CPU上部署的方案&#xff0c;但效果却不是让人那么满意&#xff0c…

杭州一公司开出20万月薪/320万年薪抢人!ChatGPT掀起AI热潮,AIGC人才被爆抢

5年工作经历&#xff0c;博士学位&#xff0c;最高月薪20万。 最近&#xff0c;位于杭州未来科技城一家公司&#xff0c;开出了最高320万年薪&#xff0c;招聘AIGC方向算法工程师一名。 ChatGPT在全球掀起了AI热潮&#xff0c;国内互联网大厂纷纷加入战局打造国内版ChatGPT。…

Analysis For Office的一些使用技巧

目录 1. 自由特性下钻停止刷新 2. 直接双击过滤内容 3.重复层级值 4. 从过滤值选参数 5.从Excel复制参数 6. 保存参数值 7.计划值回退到上一步 8. 保存当前导航步骤 1. 自由特性下钻停止刷新 一般我们每次拖一个自由特性到workbook里&#xff0c;报表都会自动刷新。如…

电商败给了直播带货,实体店能靠直播提升销量吗?

根据官方发布的最新数据&#xff0c;直播带货的市场规模将达到4.7万亿元&#xff0c;不少品牌借助直播间触达了上亿的用户&#xff0c;完成了千万级别的销售订单。 目前直播带货已经成为“红海”市场&#xff0c;未来仍然有巨大的发展空间。 一、直播带货打败传统电商 自从电商…

vue3+ts数组去重方法-reactive/ref响应式显示

vue3ts数组去重方法-reactive/ref响应式显示 本文目录 vue3ts数组去重方法-reactive/ref响应式显示简单数组使用 Set 和 扩展运算符&#xff08;...&#xff09;将集合转换回数组使用 Set 和 Array.from() 方法将集合转换回数组使用 filter 和 indexOf 进行判断使用 splice 和 …

函函函函函函函函函函函数——two

&#x1f929;本文作者&#xff1a;大家好&#xff0c;我是paperjie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 &#x1f970;内容专栏&#xff1a;这里是《C知识系统分享》专栏&#xff0c;笔者用重金(时间和精力)打造&#xff0c;基础知识一网打尽&#xff0c;…

【DevOps视频】笔记】1. DevOps的诞生

视频官网 目录 一、DevOps介绍 定义&#xff1a; 作用&#xff1a; 核心&#xff1a; 二、软件开发流程 三、流程图 一、DevOps介绍 定义&#xff1a; Development & Operations的缩写&#xff0c;也就是开发&运维DevOps 是一个不断提高效率并且持续不断工作…

(八)Geoprocessing地理处理框架——基本介绍

&#xff08;八&#xff09;Geoprocessing地理处理框架——基本介绍 目录 &#xff08;八&#xff09;Geoprocessing地理处理框架——基本介绍 1.基本概念2.集成ArcToolbox3.多种使用方式3.1对话框3.2Python窗口3.3模型构建器3.4脚本 4.应用基础4.1启动ArcToolbox4.2激活扩展工…

PostgreSQL中创建索引的消极影响

相信大家在使用数据库中&#xff0c;提高SQL查询速度最简单的办法就是添加相关索引&#xff0c;但是其实我们创建的索引并不一定能用上&#xff0c;有时候顺序扫描也并不见的就比离散的索引扫描差&#xff0c;任何事物我们要辩证的看待&#xff0c;今天我们说明一下创建索引的一…

AQS 抽象同步队列的简单理解

前置知识&#xff1a; 可重入锁又叫递归锁&#xff0c;同一个线程在外层方法获取锁的时候&#xff0c;在进入该线程内层方法会自动获取锁&#xff0c;不会因为之前已经获取过还没释放就阻塞 同一个线程可以多次获得同一把锁 每个锁对象都有一个锁计数器和一个指向持有该锁的…

Python爬虫-D车网近半年(六个月)汽车销量排行榜

前言 本文是该专栏的第46篇,后面会持续分享python爬虫干货知识,记得关注。 在本专栏前面,笔者有单独详细介绍过该平台当前月更新的最新汽车销量排行榜数据。感兴趣的同学,可往前翻阅查看(Python爬虫-某懂车平台之汽车销量排行榜)。 而本文,笔者将详细来介绍该平台近半年…

【git】git提交代码-使用简易指南

1、创建新仓库&#xff1a;git init 创建新文件夹&#xff0c;打开执行&#xff1a;git init &#xff0c;创建新的git仓库 2、检出仓库&#xff1a;git clone 执行该命令&#xff0c;以创建一个本地仓库的克隆版本&#xff1a;git clone /path/to/repository 如果是远端服务器…

一文解析Systrace 的原理、流程及定制

一、原理和基本流程 Systrace是android性能调试优化的常用工具&#xff0c;它可以收集进程的活动信息&#xff0c;如界面布局、UI渲染、binder通信等&#xff1b;也可以收集内核信息&#xff0c;如cpu调度、IO活动、中断等&#xff1b;这些信息会统一时间轴&#xff0c;在Chro…

【ROS仿真实战】Gazebo仿真平台介绍及安装方法(一)

文章目录 前言一、Gazebo简介二、Gazebo仿真平台的基本概念三、Gazebo仿真平台的安装方法四、总结 前言 Gazebo仿真平台是一个广泛应用于机器人研发、测试和教育等领域的开源软件。它可以模拟机器人的运动、感知和控制等行为&#xff0c;并提供了丰富的物理引擎、传感器模拟和…

PYQT5学习笔记05——QObject父子对象API以及案例

一、父子对象API 我们在这里简单演示一下父子对象API的具体用法以及代码实现&#xff0c;父子对象API有五个&#xff0c;分别是setParent、parent、children、findChild、findChildren&#xff0c;接下来对每一个API都具体演示一下。 1、setParent(parent)和parent() setParen…

国外导师对博士后申请简历的几点建议

正所谓“工欲善其事&#xff0c;必先利其器”&#xff0c;想要申请国外的博士后职位&#xff0c;就要准备好相应的申请文书材料。如果说Cover Letter是职位的窍门砖&#xff0c;那么申请者的简历就是争取职位的决定性筹码。 相信大家已经看过许多简历的模版了&#xff0c;但是…