【STL】模拟实现反向迭代器

目录

1. 读源码

2. 搭建框架 

3. 迭代器的操作

operator*() 

operator->()

operator++()

operator--()

operator!=()

4. 实现 list 的反向迭代器

5. 实现 vector 的反向迭代器

6. 源码分享

写在最后:


1. 读源码

我们之前实现的 vector,list 好像都只实现了他们的正向迭代器,那有正向,

会有反向迭代器这种东西吗?答案是有的,那我们该怎么实现呢?

实际上根据我们之前的经验,根据每个容器的特色实现一份就好了,

但是之前在读源码的时候,好像并没有看到源码实现,这是怎么一回事呢?

来读读 list 的源码是怎么做的:

我们在这里找到了 list 的反向迭代器,他没有自己实现,

而是直接使用了这个 reverse_iterator 的类模板,这又是怎么一回事呢?

我们在 STL 源码的 stl_iterator.h 文件中找到了答案:

这里面竟然存在这样一个类,reverse_iterator,难道说其他的容器,

也都不是自己实现一份反向迭代器,而是直接使用这个类吗?

想到这里,我们赶紧去看一眼 vector 的源码:

发现了没有,他们的调用方式是一模一样的,都是同样的调用,

使用的类型是他们各自的迭代器类型,通过模板的特性,

让编译器实例化出不同的代码,达成实现他们各自的迭代器的目的,

非常巧妙的设计,只一份代码,让所有的容器的反向迭代器都复用,

那他是怎么实现的呢?我们来看看他实现的源码:

我们可以看到这个就是迭代器类型的成员变量了。

还是老规矩,看完成员变量,我们去看一下核心的接口实现,

先来看 ++ 的重载 :

他的 ++ 就是让原先的正向迭代器执行 -- 操作,没毛病,

再来看 -- 的重载:

也没毛病,我们再来看看他的解引用操作:

这下怪了,为啥解引用取到的位置是原先位置的前一个位置呢? 

那我们就得去看看那些容器的反向迭代器是怎么确定初始位置的了,

先来看 list 的:

正向迭代器的 begin 就是第一个位置,end 就是哨兵位的头结点,

但是 rbegin 用的是 end 来初始化反向迭代器,也就是他在 end 的位置(哨兵位上)

而 rend 用的是 begin 来初始化反向迭代器,也就是他在 begin 的位置(第一个位置上)

我们也可以看一下 vector 是不是这样的:

可以看到也是这样的。

那这样好像不太对啊,如果直接使用迭代器,

第一次解引用就会出问题:(这里我写段伪代码感受一下)

rit =  rbegin()

while(rit != rend()) {

        cout << *rit << endl; // 这里就出问题了

        rit++;

}

所以库里在实现的解引用操作符的时候,解引用的就是他的前一个位置,

我再截出来看一眼:

现在看的也差不多了,马上开始动手实现吧~

2. 搭建框架 

namespace xl {template <class Iterator>class reverse_iterator {private:Iterator _it;public:reverse_iterator(Iterator it): _it(it){}};
}

其实框架就一点点啦,

就是写个成员变量,写个构造函数就差不多完成啦。

3. 迭代器的操作

operator*() 

我们直接上手:

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

这个时候我们遇到了一些困难,

我们该怎么返回这个值呢?还是跟之前一样我们需要让他支持 const 类型,

先来看看源码是怎么实现的:

他这里的 reference 通过了一系列的复杂操作然后套了出来,

这里他用的操作是萃取,比较的麻烦,还涉及模板的特化,

所以我们打算用一个比较方便暴力的方法,就是通过模板参数直接传过来:

 

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

这样下面的实现也解决了:

operator->()

Ptr operator->() {return &(operator*());
}

operator++()

这里我们再顺便把类名 typedef 一下,因为他太长了:

然后来实现 ++ :(这里我都是前置和后置都实现了)

self& operator++() {--_it;return *this;
}self operator++(int) {self tmp = _it;--_it;return tmp;
}

operator--()

self& operator--() {++_it;return *this;
}self operator--(int) {self tmp = _it;++_it;return tmp;
}

operator!=()

直接用正向迭代器的 != 比较即可:

bool operator!=(const self& s) {return _it != s._it;
}

4. 实现 list 的反向迭代器

typedef reverse_iterator<iterator, const T&, const T*> const_reverse_iterator;
typedef reverse_iterator<iterator, T&, T*> reverse_iterator;reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const { return reverse_iterator(begin()); }

 我们来测试一下:

#include "iterator.h"
#include "list.h"int main()
{xl::list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);for (auto e : lt) cout << e << " ";cout << endl;xl::list<int>::reverse_iterator rit = lt.rbegin();while (rit != lt.rend()) {cout << *rit << " ";++rit;}cout << endl;return 0; 
}

输出:

输出没毛病~ 

5. 实现 vector 的反向迭代器

跟 list 一模一样的操作(直接把代码粘过来即可)

然后我们直接测试:

void test_vector() {xl::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto e : v) cout << e << " ";cout << endl;xl::vector<int>::reverse_iterator rit = v.rbegin();while (rit != v.rend()) {cout << *rit << " ";++rit;}cout << endl;
}

输出:

结果也没毛病~ 

6. 源码分享

模拟实现简易STL: 模拟实现简易STL (gitee.com)

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。

如果感到有所收获的话可以给博主点一个哦。

如果文章内容有遗漏或者错误的地方欢迎私信博主或者在评论区指出~

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

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

相关文章

类加载机制,类加载顺序

类加载顺序 ①类加载从上往下执行&#xff0c;依次执行静态的初始化语句和初始化块&#xff0c;而且类加载优先于对象创建。&#xff08;静态初始化语句和初始化块只加载一次&#xff09; ②创建本类的对象时&#xff0c;从上往下执行一次非静态的初始化语句和初始化块&#…

Unity《勇士传说》开发日记:如何制作可互动标识

要实现的需求&#xff1a; 在游戏当中&#xff0c;我们的主角走到宝箱前&#xff0c;可以将宝箱打开&#xff0c;走到洞穴口可以进入下一个场景&#xff0c;此时需要有个互动标识来提示用户。如图所示&#xff1a; 当角色走到宝箱前&#xff0c;弹出互动标识提示用户按下E键可…

关于idea如何成功运行web项目

导入项目 如图 依次选择 file - new - Project from Existing Sources 选择存放的项目目录地址 如图 导入完成 点击ok 如图 依次选择 Create project from existing sources 点击next如图 &#xff0c;此处默认即可 点击 next如图 点击next有该提示 是因为之前导入过…

jmeter接口测试、压力测试简单实现

jmeter测试的组件执行顺序&#xff1a; 测试计划—>线程组—>配置元件—>前置处理器—>定时器—>逻辑控制器—>取样器—>后置处理器—>断言—>监听器 组件的作用范围&#xff1a; 同级组件同级组件下的子组件父组件 目前市面上的三类接口 1、基…

10分钟带你实现一个Android自定义View:带动画的等级经验条

先展示一下静态效果图 介绍一下我们的实现流程&#xff1a; 首先整个经验条有一个圆角边框的背景打底&#xff1b;然后给经验条绘制一条轨道&#xff0c;让用户比较直观地看到总进度的长度&#xff1b;在轨道的上层绘制我们的渐变色经验条&#xff1b;在经验条的上层绘制等级…

用html+javascript打造公文一键排版系统8:附件及标题排版

最近工作有点忙&#xff0c;所 以没能及时完善公文一键排版系统&#xff0c;现在只好熬夜更新一下。 有时公文有包括附件&#xff0c;招照公文排版规范&#xff1a; 附件应当另面编排&#xff0c;并在版记之前&#xff0c;与公文正文一起装订。“附件”二字及附件顺序号用3号黑…

Python(四十六)列表

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

【Java基础教程】(四十八)集合体系篇 · 上:全面解析 Collection、List、Set常用子接口及集合元素迭代遍历方式~【文末送书】

Java基础教程之集合体系 上 &#x1f539;本章学习目标1️⃣ 类集框架介绍2️⃣ 单列集合顶层接口&#xff1a;Collection3️⃣ List 子接口3.1 ArrayList 类&#x1f50d; 数组&#xff08;Array&#xff09;与列表&#xff08;ArrayList&#xff09;有什么区别?3.2 LinkedL…

在 ArcGIS Pro 中使用 H3 创建蜂窝六边形

H3是Uber开发的分层索引系统,它使用六边形来平铺地球表面。H3在二十面体(一个具有20个三角形面和12个顶点的形状)上构建其六边形网格。由于仅用六边形不可能平铺二十面体,因此每个分辨率需要12个五边形来完成网格。分层索引网格意味着每个六边形都可以细分为子单元六边形。…

给jupter设置新环境

文章目录 给jupternotebook设置新环境遇到的报错添加路径的方法 给jupternotebook设置新环境 # 先在anaconda界面新建环境 conda env list # 查看conda prompt下的有的环境变量 带星号的是当前活跃的 activate XXXX pip install ipykernel ipython ipython kernel install --u…

如何安装mmcv?官网解答

pip install -U openmim mim install mmcv

【高分论文密码】大尺度空间模拟预测与数字制图教程

详情点击链接&#xff1a;【高分论文密码】大尺度空间模拟预测与数字制图 一&#xff0c;R语言空间数据及数据挖掘关键技术 1、R语言空间数据及应用特点 1)R语言基础与数据科学 2)R空间矢量数据 3)R栅格数据 2、R语言空间数据挖掘关键技术 二&#xff0c;R语言空间数据高…

素描基础知识

素描基础入门 1.基础线条 1.1 握笔姿势及长线条 2.排线 2.1 不同姿势画排线 2.1.1 姿势画排线 2.1.2 用手腕画排线 2.1.3 小拇指画排线 2.1.4 叠加排线 2.1.5交叉排线 2.2 纸张擦法 2.3 排线学习榜样 2.4 四种常见的排线 3、定向连线 4、一点透视 4.1 透视的规律 4.2 焦点透视…

SpringCloudAlibaba:服务网关之Gateway的cors跨域问题

目录 一&#xff1a;解决问题 二&#xff1a;什么是跨域 三&#xff1a;cors跨域是什么&#xff1f; 一&#xff1a;解决问题 遇到错误&#xff1a; 前端请求时报错 解决&#xff1a; 网关中添加配置文件&#xff0c;注意springboot版本&#xff0c;添加配置。 springboo…

Hive 调优集锦(1)

一、前言 1.1 概念 Hive 依赖于 HDFS 存储数据&#xff0c;Hive 将 HQL 转换成 MapReduce 执行&#xff0c;所以说 Hive 是基于Hadoop 的一个数据仓库工具&#xff0c;实质就是一款基于 HDFS 的 MapReduce 计算框架&#xff0c;对存储在HDFS 中的数据进行分析和管理。 1.2 架…

删除每行中的最大值

给你一个 m x n 大小的矩阵 grid &#xff0c;由若干正整数组成。 执行下述操作&#xff0c;直到 grid 变为空矩阵&#xff1a; 从每一行删除值最大的元素。如果存在多个这样的值&#xff0c;删除其中任何一个。 将删除元素中的最大值与答案相加。 注意 每执行一次操作&…

HBase有写入数据,页面端显示无数据量

写了一个测试类&#xff0c;插入几条数据&#xff0c;测试HBase的数据量。很简单的功能&#xff0c;这就出现问题了。。网页端可以看到&#xff0c;能够看到读写请求&#xff0c;但是不管是内存、还是磁盘&#xff0c;都没有数据。 于是就想到去HDFS查看&#xff0c;也是有数据…

windows命令行

参考:https://blog.csdn.net/u014419722/article/details/130427423 1、 创建文件夹&#xff08;mkdir或md&#xff09; 创建单个文件&#xff1a;mkdir cmd_test 创建二级文件&#xff1a;mkdir cmd_test\456\123 创建多个文件&#xff1a;mkdir cmd_test\000 cmd_test\111 2…

idea快速运行vue项目

目录 一、前提 二、步骤 安装vue.js插件 添加脚本 进行如下配置 一、前提 安装好node.js环境并初始化完成和安装好依赖 二、步骤 安装vue.js插件 打开idea,然后在File–Settings–Plugins–Makerplace下找到vue.js插件,安装并重启idea 添加脚本 进行如下配置 在Sctipts中根…