C++ STL学习之【反向迭代器】

news2024/12/23 19:45:20

✨个人主页: 夜 默
🎉所属专栏: C++修行之路
🎊每篇一句: 图片来源

  • A year from now you may wish you had started today.
    • 明年今日,你会希望此时此刻的自己已经开始行动了。

屹立不倒


文章目录

  • 🌇前言
  • 🏙️正文
    • 1、反向迭代器设计
      • 1.1、反向思想
      • 1.2、多参数模板
      • 1.3、极致对称
      • 1.4、其他功能
    • 2、应用于 vector
    • 3、应用于 list
    • 4、源码
  • 🌆总结


🌇前言

适配器模式是 STL 中的重要组成部分,在上一篇文章中我们学习了 容器适配器 的相关知识,即 stackqueue,除了 容器适配器 外,还有 迭代器适配器,借助 迭代器适配器,可以轻松将各种容器中的普通迭代器转变为反向迭代器,这正是适配器的核心思想

千兆网卡转换器


🏙️正文

反向迭代器适用于所有的容器,因此它是作为一个单独的 .h 文件出现的,别的容器如果想使用,直接包含就行了

1、反向迭代器设计

反向迭代器 reverse_iterator 可以用来反向遍历容器,在某些场景下很实用

反向迭代器
反向迭代器类中需要有:正向迭代器对象构造函数

template<class Iterator>
struct __reverse_iterator
{
	Iterator _cur;	//正向迭代器类

	//需要借助构造函数,构成出正向迭代器
	__reverse_iterator(Iterator cur)
		:_cur(cur)
	{}
};

注意源码中的反向迭代器设计较为复杂,涉及 萃取 等操作,为了方便学习,这里实现的是简易版本

1.1、反向思想

何谓反向?与正向相反就是反向,比如时钟正常都是顺时针转,但如果时钟逆时针选择,此时就称为反方向的钟

反方向的钟

存在 vector<int>() = {1, 2, 3, 4, 5} 不同方向的遍历结果不同

正向迭代器:正向遍历
结果:1 2 3 4 5

反向迭代器:反向遍历
结果:5 4 3 2 1

图解

注:库中的反向迭代器在设计时,为了最求极致的对称,rbegin() 指向最后一个有效元素的下一个位置,rend() 指向第一个有效元素(位置是与正向迭代器相反的)

//_cur 为正向迭代器
self& operator++()
{
	--_cur;	//你要++,我就--
	return *this;
}
self operator++(int)
{
	Iterator tmp = _cur;
	--_cur;
	return tmp;
}

self& operator--()
{
	++_cur;	//你要--,我就++,反过来操作
	return *this;
}
self operator--(int)
{
	Iterator tmp = _cur;
	++_cur;
	return tmp;
}

1.2、多参数模板

在模拟实现 list 迭代器类时,为了解决普通对象与 const 对象的代码冗余问题,引入了多参数,通过对形参传递不同的对象,变换为不同属性的迭代器;在反向迭代器类重,这一种巧妙思想也得到了继承

template<class Iterator, class Ref, class Ptr>
struct __reverse_iterator
{
	typedef __reverse_iterator<Iterator, Ref, Ptr> self;	//重命名迭代器类为 self
	Iterator _cur;	//正向迭代器类
	
	//……
};

在涉及 operator*() 时,需要返回目标对象引用,使用 Ref;同理,在涉及 operator->() 时,需要返回目标对象指针,使用 Ptr

具体返回对象(引用 / 指针)是否为 const 修饰,取决于调用方

1.3、极致对称

在反向迭代器类中,有一个十分奇怪的函数 operator*(),它返回的并非当前所指向的对象,而且上一个对象

Ref operator*()
{
	Iterator tmp = _cur;
	return *--tmp;	//返回的是上一个对象
}

原因:大佬在设计时为了追求与正向迭代器的绝对对称,故意指向位置与其保持一致,仅仅是 rend()begin() 处,rbegin()end()

反向迭代器
经过这样设计后,rbegin()rend() 函数的实现就变得简单了,此时压力给到了 operator*() 的实现

reverse_iterator rbegin() { reverse_iterator(end()); }	//开始 -> 尾
reverse_iterator rend() { reverse_iterator(begin()); }	//结束 -> 头

1.4、其他功能

假设想通过迭代器直接访问自定义对象中的成员时,需要用到 operator->() 函数,作用是取出迭代器所指向对象的指针 Ptr

Ptr operator->()
{
	return &(operator*());	//采取复用的形式
}

迭代器还需要比较函数 operator==()operator!=(),具体实现时,都是在复用具体对象的比较函数

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

以上就是反向迭代器所必须的基础功能,如果你还想实现更多比较逻辑,如 operator<() 等,可以自己实现

反向迭代器类的完整代码:

#pragma once

namespace Yohifo
{
	template<class Iterator, class Ref, class Ptr>
	struct __reverse_iterator
	{
		typedef __reverse_iterator<Iterator, Ref, Ptr> self;	//重命名迭代器类为 self
		Iterator _cur;	//正向迭代器类

		__reverse_iterator(Iterator cur)
			:_cur(cur)
		{}

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

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

		//_cur 为普通(正向)迭代器
		self& operator++()
		{
			--_cur;
			return *this;
		}
		self operator++(int)
		{
			Iterator tmp = _cur;
			--_cur;
			return tmp;
		}

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

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

编写完成此头文件 reverse_iterator.hpp 后,任何具有正向迭代器的容器,都可以利用迭代器适配器,适配出属于自己的反向迭代器

具体使用例子可以接着往下看


2、应用于 vector

vector 模拟实现中,引入头文件 reverse_iterator.hpp,定义出反向迭代器所必须的函数

#pragma once
#include <iostream>
#include <string>
#include <assert.h>
#include <vector>	//对比测试用
#include <algorithm>	//排序所需要的头文件
#include <functional>	//仿函数头文件
#include "reverse_iterator.hpp"	//使用反向迭代器必须的头文件

using std::cin;
using std::cout;
using std::endl;
using std::string;

template<class T>
class vector
{
public:
	//……

	//=====反向迭代器=====
	typedef __reverse_iterator<iterator, T&, T*> reverse_iterator;
	typedef __reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

	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()); }
	
	//……

private:
	iterator _start;	//指向起始位置
	iterator _finish;	//指向有效元素的下一个位置
	iterator _end_of_storage;	//指向可用空间的下一个位置
};

通过反向迭代器进行遍历

void TestVector9()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	vector<int> v(arr, arr + sizeof(arr) / sizeof(arr[0]));

	vector<int>::reverse_iterator rit = v.rbegin();
	while (rit != v.rend())
	{
		cout << *rit << " ";
		++rit;	//反向迭代器++,就是--
	}
	cout << endl;
}

结果
可以成功使用反向迭代器进行遍历


3、应用于 list

既然是迭代器适配器,那么反向迭代器也可以适用于 list

#pragma once
#include <iostream>
#include <cassert>
#include <vector>
#include "reverse_iterator.hpp"	//使用反向迭代器

using namespace std;

//……

//list本类
template<class T>
class list
{
	typedef __list_node<T> node;
	typedef T value_type;
	typedef T& refence;
	typedef const T& const_refence;
public:

	//……
	
	//=====反向迭代器=====
	typedef __reverse_iterator<iterator, T&, T*> reverse_iterator;
	typedef __reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

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

	//……
	
private:
	//初始化出头节点
	void empty_init()
	{
		_head = new node;
		_head->_prev = _head->_next = _head;
	}

	node* _head;	//哨兵位节点
};

通过反向迭代器对自定义类型数据进行遍历

struct B
{
	B(int a = 0, char c = 0)
		:_a(a)
		,_c(c)
	{}

	int _a;
	char _c;
};

void TestList()
{
	list<B> lb;

	lb.push_back(B(1, 'a'));
	lb.push_back(B(2, 'b'));
	lb.push_back(B(3, 'c'));

	list<B>::reverse_iterator rit = lb.rbegin();
	while (rit != lb.rend())
	{
		cout << "_a: " << rit->_a << " | " << "_c: " << rit->_c << endl;
		++rit;	//即使是反向迭代器,也是++
	}
	cout << endl;
}

结果
此时主要是用到了 operator->() 访问自定义类型中的成员变量


4、源码

关于 vectorlist (迭代器版)的源码在下面仓库中

vector(反向迭代器版)

list(反向迭代器版)


🌆总结

以上就是本篇关于 C++ STL 学习之【反向迭代器】的全部内容了,在本篇文章中,我们主要学习了反向迭代器类的思想及实现,最后分别用了 vectorlist 进行了测试,成功实现了反向遍历

如果你觉得本文写的还不错的话,可以留下一个小小的赞👍,你的支持是我分享的最大动力!

如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正


星辰大海

相关文章推荐

STL 之 适配器

C++ STL学习之【容器适配器】

===============

STL 之 list 类

C++ STL学习之【list的模拟实现】

C++ STL学习之【list的使用】

===============

STL 之 vector 类

C++ STL学习之【vector的模拟实现】

C++ STL学习之【vector的使用】

感谢支持

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

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

相关文章

[持续更新]mac使用chatgpt的几种方法~

1. monica 使用edge浏览器或者chrome浏览器&#xff0c;直接在官网下载即可&#xff0c;网址直通&#xff1a; bing: https://www.microsoft.com/zh-cn/edge/download?formMA13FJ google&#xff1a; Google Chrome 网络浏览器 备注&#xff1a;你需要先搭上梯子哈 安装打…

【数据库多表操作】sql语句基础及进阶

常用数据库&#xff1a; 数据库&#xff08;Database&#xff09;是按照数据结构来组织、存储和管理数据的仓库&#xff0c;它是长期存储在计算机内、有组织、有结构的数据集合。数据库是信息系统的核心部分&#xff0c;现代软件系统中大量采用了数据库管理系统&#xff08;DBM…

Windows下 influxdb 数据库安装和简单使用

步骤 1&#xff1a;安装 InfluxDB 你可以从 InfluxDB 的 InfluxDB官网winndows二进制安装包下载适用于不同操作系统的 InfluxDB 安装包。在本教程中&#xff0c;我们将介绍在 Windows上安装 InfluxDB 的步骤。 如果所示&#xff0c;可以点击下载windows版本的安卓版&#xff…

VSCode编译器环境下,基于vite+vue调试Cesium

VSCode编译器环境下&#xff0c;基于vitevue调试Cesium 1.创建一个vite项目 以官网作为参考&#xff1a;创建项目 # npm 6.x npm create vitelatest my-vue-app --template vue# npm 7, extra double-dash is needed: npm create vitelatest my-vue-app -- --template vue#…

https页面加载http资源的解决方法

文章目录 1.报错如图2.项目背景3.网上的解决方案4.我的最终解决方案 1.报错如图 2.项目背景 我们的项目采用的全是https请求&#xff0c;而使用第三方文件管理器go-fastdfs&#xff0c;该文件管理器返回的所有下载文件的请求全是http开头的&#xff0c;比如http://10.110.38.25…

计算机组成原理/数据库补充 存储器第四章---虚拟内存

刚刚数据库下课讲了很多有关虚拟内存的东西感觉很多都忘了&#xff0c;现在写这篇文章来复习一下 为什么要引入虚拟内存 在计算机系统中&#xff0c;多个进程共享CPU和内存&#xff0c; 如果太多的进程需要过多的内存空间&#xff0c;那么其中一部分进程就会无法或得足够得空…

2023年网络安全比赛--Windows渗透测试中职组(超详细)

一、竞赛时间 180分钟 共计3小时 二、竞赛阶段 1.通过本地PC中渗透测试平台Kali对服务器场景20221219win进行系统服务及版本扫描渗透测试,并将该操作显示结果中1433端口对应的服务版本信息作为Flag值(例如3.1.4500)提交; 2.通过本地PC中渗透测试平台Kali对服务器场景202212…

chatgpt智能提效职场办公-ppt怎么全屏

作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; 在PowerPoint中&#xff0c;可以通过以下几种方法将演示文稿切换到全屏模式&#xff1a; 方法1&#xff1a;按F5键 在编辑演示文稿的状…

基于LS1028 TSN 交换机硬件系统设计与实现(二)

3.1 LS1028A 芯片研究 目前市面上支持 TSN 系统的芯片较少&#xff0c;其中两家较大的公司之一博通 &#xff08; Broadcom &#xff09; 2017 年推出了 StrataConnect BCM53570 系列的以太网交换机&#xff0c;该系 列支持的新技术旨在帮助用户应对物联网、汽车网络和…

mybatis的原理详解

mybatis的原理详解 原理图 执行的原理图如下图所示&#xff1a; 配置文件分析 config.xml: <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.or…

PTA L1-091 程序员买包子 (10 分)

这是一条检测真正程序员的段子&#xff1a;假如你被家人要求下班顺路买十只包子&#xff0c;如果看到卖西瓜的&#xff0c;买一只。那么你会在什么情况下只买一只包子回家&#xff1f; 本题要求你考虑这个段子的通用版&#xff1a;假如你被要求下班顺路买 N N N 只包子&#x…

华为云上云实践(二):Linux 环境下对云硬盘 EVS 的创建、挂载和初始化

本文主要讲解华为云云硬盘 EVS 的在 Linux 操作系统 EC2 服务器上创建、挂载及云硬盘初始化等基本操作&#xff0c;快速掌握华为云云硬盘 EVS 操作方法。 How to attach new Huawei EVS Volume 文章目录 一、前言二、环境准备与造作步骤2.1 本文实践操作的环境2.2 本文实践操作…

基础数据结构------单链表

1、链表使用的解决方案 【链表的概述】 链表是一种物理存储单元上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点&#xff08;链表中每一个元素称为结点&#xff09;组成&#xff0c;结点可以在运行时动态生成。…

从MySQL小表驱动大表说起

刚刚开始学习MySQL的时候&#xff0c;连表查询直接使用left join或者更粗暴点&#xff0c;两个表直接查询&#xff0c;where过滤条件&#xff0c;组装笛卡尔积&#xff0c;最终出现自己想要的结果。 当然&#xff0c;现在left join也是会用的&#xff0c;毕竟嘛&#xff0c;方便…

如何实现多存储文件传输,镭速提供多存储文件传输解决方案

目前的文件传输系统中&#xff0c;大多数采用的文件传输系统只支持单个的存储。随着科技的发展&#xff0c;存储的类型越来越多&#xff0c;构建的越来越复杂&#xff0c;业务要求越来越多样化&#xff0c;只支持单个存储的文件传输系统是无法满足现有的需求。 为实现高自由度…

Java基础(十九):集合框架

Java基础系列文章 Java基础(一)&#xff1a;语言概述 Java基础(二)&#xff1a;原码、反码、补码及进制之间的运算 Java基础(三)&#xff1a;数据类型与进制 Java基础(四)&#xff1a;逻辑运算符和位运算符 Java基础(五)&#xff1a;流程控制语句 Java基础(六)&#xff1…

vue3新的组件

1.Fragment - 在Vue2中: 组件必须有一个根标签 - 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中 - 好处: 减少标签层级, 减小内存占用 没有写根标签&#xff0c;也没有报错&#xff0c;如果是在v2中&#xff0c;我们还需要用一个div来包裹它 …

springboot web项目统一时区方案

背景 springboot项目国际化中&#xff0c;会遇到用户选择的时间和最终存到数据库的时间不一致&#xff0c;可能就是项目开发和部署时的时区没有处理好&#xff0c;导致时间转换出现了问题。 先了解时区都有哪些&#xff1a; 1.GMT&#xff1a;Greenwich Mean Time 格林威治…

移动端适配方法:rem+vw

1.百分比设置:几乎不用 因为各种属性百分比参照物(自身/父元素/...需要去查文档)很难统计固定,所以不用百分比进行适配 2.rem单位动态html的font-size 使用rem,因为rem参考html的fz,只需要在不同的屏幕上设置不同的html的fz即可,其他地方全用rem rem的fz尺寸 媒体查询 编写…

推荐系统召回之userCF

基于用户的协同过滤算法userCF 1.1 相似度计算 通过计算用户之间的相似度。这里的相似度指的是两个用户的兴趣相似度。 假设对于用户u uu和v vv&#xff0c;N ( u ) N(u)N(u)指的是用户u uu喜欢的物品集合&#xff0c;N ( v ) N(v)N(v)指的是用户v vv喜欢的物品集合&#xff0…