【C++】开始了解反向迭代器

news2024/11/25 21:26:46

在这里插入图片描述
送给大家一句话:
重要的东西眼睛是看不到的 — 《小王子》


反向迭代器

  • 1 前言
  • 2 反向迭代器
  • 3 复刻反向迭代器
    • 3.1 加减操作
    • 3.2 判断操作
    • 3.3 访问操作
  • 4 链表的反向迭代器
  • Thanks♪(・ω・)ノ谢谢阅读!!!
  • 下一篇文章见!!!

1 前言

在复刻STL中的list容器时,我们首次采用了类封装的方式来构建迭代器,以此实现迭代器的递增、递减和元素访问功能。然而,当我们面临实现反向迭代器的需求时,是否需要重头开始,再次进行类的封装呢?

显然这种做法并非必要(不然就要手搓无数个反向迭代器了)。因为反向迭代器与正向迭代器在功能上存在高度一致性,唯一的区别在于它们在容器中的移动方向相反。因此,我们可以采用适配器设计模式,对现有的正向迭代器进行二次封装,以此满足反向迭代器的需求。

通过引入适配器,我们不仅可以避免重复造轮子的工作,还能够提升代码的复用性和简洁性。这种设计模式的应用,使得我们能够在保持代码高效和可维护性的同时,轻松实现反向迭代器的功能。

2 反向迭代器

我们先来看源码中是如何实现的:

template <class RandomAccessIterator, class T, class Reference = T&,
          class Distance = ptrdiff_t> 
#else
template <class RandomAccessIterator, class T, class Reference,
          class Distance> 
#endif
class reverse_iterator {
  typedef reverse_iterator<RandomAccessIterator, T, Reference, Distance>
        self;
protected:
  RandomAccessIterator current;
public:
  typedef random_access_iterator_tag iterator_category;
  typedef T                          value_type;
  typedef Distance                   difference_type;
  typedef T*                         pointer;
  typedef Reference                  reference;
 }

其想要通过提供的正向迭代器实现所有容器的反向迭代器。

这是链表中的反向迭代器:

  typedef reverse_bidirectional_iterator<const_iterator, value_type,
  const_reference, difference_type>
  const_reverse_iterator;
  typedef reverse_bidirectional_iterator<iterator, value_type, reference,
  difference_type>
  reverse_iterator; 

给链表的正向迭代器,就给出链表的反向迭代器。

接下来我们也来实现一下自己的反向迭代器:

3 复刻反向迭代器

通过对反向迭代器的设计模式的了解,我们可以大致写一个框架:

namespace bit
{
	// 适配器 -- 复用
	//给谁的正向迭代器就产生谁的正向迭代器
	template<class Iterator, class Ref, class Ptr>
	struct Reverse_iterator
	{
		//简化书写
		typedef Reverse_iterator<Iterator, Ref, Ptr> Self;
		//构造函数
		Reverse_iterator(Iterator it)
			:_it(it)
		{}
		//实例化一个正向迭代器
		Iterator _it;
	};
}

反向迭代器与正向迭代器在功能上相似,都用于遍历容器中的元素。然而,它们在操作方向上存在显著差异:

  • 正向迭代器通过++运算符向前移动,而反向迭代器则通过–运算符向后移动。

实现反向迭代器的基本方法是通过编写一个类模板,该模板会被编译器用来生成具体容器对应的迭代器实例。在这个过程中,编译器负责实例化这些迭代器,从而提供一种便捷的方式来反向遍历容器中的元素。

3.1 加减操作

根据反向迭代器的性质,我们可以借助正向迭代器的函数来实现反向迭代器的加减操作。

		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;
		}

通过反向使用正向迭代器的加减操作,反向加就是正向减,反向减就是正向加。

3.2 判断操作

对于反向迭代器的== !=操作实质上也就是其封装的正向迭代器的比较:

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

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

这样比较就可以了。

3.3 访问操作

这个访问操作是由说法的:


		Ref operator*()
		{
			Iterator tmp = _it;
			return  *(--tmp);//下面进行解释
		}
		//会进行省略->
		Ptr operator->()
		{
			return &(operator*());
		}

为什么这里的访问要有--操作???因为为了与正向迭代器对称,反向迭代器的开始位置并不是结尾,而是哨兵位。
在这里插入图片描述
下面这种可以直接使用已有的end() , begin()函数进行复用,增加代码可读性。所以对应的访问方式就要减一再访问。效果其实两种区别不大,但是第二种的代码更加简洁。

4 链表的反向迭代器

我们来在链表里实现一下反向迭代器(记得包含对应头文件):
首先先实例化两种反向迭代器:

typedef Reverse_iterator<iterator , T&, T*> reverse_iterator;
typedef Reverse_iterator<const_iterator , const T&, const T*> const_reverse_iterator;

接着通过相应的rend() rbegin()函数:

reverse_iterator rbegin()  { return reverse_iterator(_head); }
reverse_iterator rend()  { return reverse_iterator(_head->_next); }

const_reverse_iterator rbegin() const { return const_reverse_iterator(_head); }
const_reverse_iterator rend() const { return const_reverse_iterator(_head->_next); }

这样就可以访问了:

#include"List.h"
#include<iostream>

using namespace bit;
using namespace std;

int main()
{
	list<int> lt ;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	list<int>::reverse_iterator rit = lt.rbegin();

	while (rit != lt.rend())
	{
		cout << (*rit) << " ";
		rit++;
	}

	return 0;
}

来看效果:
在这里插入图片描述
很好,成功访问!!!
这样我们就实现反向迭代器,大家可以在实际中继续体会。

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

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

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

相关文章

SQVI创建以及生成程序

SAP数据快速查询工具&#xff1a;Sqvi-QuickView 项目实施&运维阶段&#xff0c;为了快速获取一些透明表数据&#xff0c;一开始接触项目肯定会通过大量的数据表查找&#xff0c;然后线下通过EXCEL通过VLOOKUP进行数据关联&#xff0c;这种方式在关联数据较少的情况比较适应…

【源码】2024新版二开版抢单刷单系统,前端简体、繁体双语言-支持倒计时抢单,后台指定派单连单卡单

CD&#xff1a;获取方式联系小编 微信&#xff1a;uucodes 公众号&#xff1a;资源猿 小编提供资源代找&#xff0c;环境搭建&#xff0c;源码部署调试等业务&#xff0c;需要的可以联系

APP广告变现项目要怎么去做,需要考虑哪些方面!!

要开始一个APP广告变现项目&#xff0c;您可以按照以下步骤进行操作&#xff1a; 制定商业计划&#xff1a;确定您的目标市场、目标受众和变现方式。了解竞争对手和市场趋势&#xff0c;并制定相应的推广策略。 开发APP&#xff1a;找到合适的开发团队或开发者来设计和开发您…

机器学习在安全领域的应用:从大数据中识别潜在安全威胁

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向的学习指导…

微纤维眼镜清洁布的革命性进化

在日常生活中&#xff0c;眼镜是许多人不可或缺的日常用品&#xff0c;无论是视力矫正还是防护眼睛免受阳光的伤害。然而&#xff0c;眼镜的清洁常常是一个令人头疼的问题&#xff0c;特别是在面对指纹、灰尘和其他污垢时。传统的清洁方法往往需要化学清洁剂&#xff0c;不仅繁…

Spring Boot 中Mybatis使用Like的使用方式和注意点

说明 模糊查询在项目中还是经常使用的&#xff0c;本文就简单整理Mybatis中使用Like进行模糊查询的几种写法以及一些常见的问题。 使用Springboot简单配置一下Mybatis&#xff0c;然后进行说明。Springboot集成Mybatis这里就不做介绍了&#xff0c;这里我们主要介绍一下在mybat…

【leetcode面试经典150题】59. 合并两个有序链表(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

Mac下XDebug安装

文章目录 1、下载对应的版本2、编译XDebug3、配置XDebug4、配置PhpStormDebug一下 前置工作 Mac下安装HomebrewMac下brew安装php7.4 1、下载对应的版本 首先按照支持的版本和兼容性来下载对应的版本&#xff0c;此表列出了仍支持哪些 Xdebug 版本&#xff0c;以及哪些版本可用…

SPI总线通讯协议

文章目录 SPIQSPISPI配置SPI读写一个字节W25Q128初始化读取SPI FLASH写SPI FLASH SPI SPI:串行外围设备接口(Serial peripheral interface)&#xff0c;一种高速&#xff0c; 全双工、同步的通信总线。 SPI使用4条线通信&#xff1a; MISO&#xff1a;主设备数据输入&#xf…

为主机配置IP

第一种方法:nmcli #nmcli connection modify eth0 ipv4.method manual ipv4.addresses 172.25.254.100/24 ipv4.gateway 172.25.254.2 ipv4.dns 114.114.114.114 autoconnect yes #nmcli c up etho //激活网卡命令(网卡早就配好,只是修改ip就不用输入这条命令了) 第二…

【解决】echarts条形图纵坐标显示不全

先说结论&#xff1a; option:{...grid: {containLabel: true},... }这个属性是控制整体的坐标标签的。加上这个就可以显示完整了。然后再根据其他属性调整标签的字体、颜色之类的 yAxis : [{...axisLabel:{width:100,overflow:break,truncate:...,color:red,fontSize:10,},..…

PHP定时任务框架taskPHP3.0学习记录5环境部署常见问题及解决方案

php版本问题 当出现一下错误&#xff0c;说明php版本不支持&#xff0c;建议升级php版本&#xff0c;至少>5.6 Failed loading /usr/local/zend/php55/ZendGuardLoader.so: /usr/local/zend/php55/ZendGuardLoader.so: undefined symbol: zval_used_for_init PHP Warning:…

计算机网络:CSMA/CA协议

计算机网络&#xff1a;CSMA/CA协议 CSMA/CA概述帧间间隔工作原理退避算法虚拟载波监听 CSMA/CA概述 讲解CSMA/CA之前&#xff0c;我们回顾一下CSMA/CD的三个特性&#xff1a; 多址接入MA&#xff1a;多个主机连接在一条总线上&#xff0c;竞争使用总线 载波监听CS&#xff1a…

Grass注册不了、按钮灰色的解决方案

近期相信grass挂机项目不少人有所有接触。还有不了解这个项目的可以看看博客&#xff1a; http://t.csdnimg.cn/bI4UO 但是不少人注册时遇到无法注册的问题&#xff0c;或者是注册按钮显示灰色&#xff0c;放上鼠标时显示禁止。这也是博主在尝试时遇到的问题。 经过探索&…

【HarmonyOS 4+NEXT】开发工具安装指南

&#x1f64b;‍ 一日之际在于晨 ⭐本期内容&#xff1a;开发工具安装 &#x1f3c6;系列专栏&#xff1a;鸿蒙HarmonyOS4NEXT&#xff1a;探索未来智能生态新纪元 文章目录 前言准备工作下载开发工具安装开发工具配置开发环境总结 前言 随着科技的不断进步&#xff0c;智能设…

Scrapy 框架基础

Scrapy框架基础Scrapy框架进阶 Scrapy 框架基础 【一】框架介绍 【1】简介 Scrapy是一个用于网络爬取的快速高级框架&#xff0c;使用Python编写他不仅可以用于数据挖掘&#xff0c;还可以用于检测和自动化测试等任务 【2】框架 官网链接https://docs.scrapy.org/en/late…

105.从前序遍历与中序遍历序列构造二叉树

力扣链接&#xff1a;105. 从前序与中序遍历序列构造二叉树 - 力扣&#xff08;LeetCode&#xff09; 问题主体&#xff1a; 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构…

Operating System Introduction

What is an Operating System? A program that acts as an intermediary between a user of a computer and the computer hardware 操作系统即用户与计算机硬件中的类似中介的程序 Operating system goals: Execute user programs and make solving user problems easier Mak…

代码随想录算法训练营DAY28|C++回溯算法Part.4|93.复原IP地址、78.子集、90.子集II

文章目录 93.复原IP地址思路确定非法的范围树形结构 伪代码 78.子集思路伪代码实现CPP代码 90.子集II思路CPP代码用used去重的办法用set去重的版本不使用used数组、set的版本 93.复原IP地址 力扣题目链接 文章讲解&#xff1a;93.复原IP地址 视频讲解&#xff1a;回溯算法如何分…

Hadoop数据压缩

Hadoop数据压缩 Hadoop 数据压缩是一种用于减少存储空间和网络传输成本的技术&#xff0c;通常应用于大数据处理场景。随着数据量的不断增长&#xff0c;对存储和网络带宽的需求也在增加&#xff0c;因此采用数据压缩技术可以有效地减少数据的存储和传输成本&#xff0c;提高数…