【c++】反向迭代器的探究实现

news2024/11/19 5:31:30

Alt

🔥个人主页Quitecoder

🔥专栏c++笔记仓

Alt

在list中我们实现了正向的迭代器,学习完优先级队列后,我们也对适配器模式有了一个深刻的理解,这篇文章基于这种模式下,实现各类容器的反向迭代器

目录

  • 1.引入:list的反向迭代器
  • 2.适配实现反向迭代器
      • 1. 构造函数:
      • 2. 解引用操作符 `operator*`:
      • 3. 成员访问操作符 `operator->`:
      • 4. 前置自增操作符 `operator++`:
      • 5. 前置自减操作符 `operator--`:
      • 6. 不等于操作符 `operator!=`:
      • 总结编译器处理:

1.引入:list的反向迭代器

首先来回顾一下我们实现list的正向迭代器:

	template<class T, class Ref, class Ptr>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;

		Node* _node;
		ListIterator(Node* node)
			:_node(node)
		{}
		// *it
		//T& operator*()
		Ref operator*()
		{
			return _node->_data;
		}
	
		// it->
		//T* operator->()
		Ptr operator->()
		{
			return &_node->_data;
		}
		// ++it
		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& it)
		{
			return _node != it._node;
		}

		bool operator==(const Self& it)
		{
			return _node == it._node;
		}
	};

在list类里面这样声明:

template<class T>
class list {
    // ... 省略其他代码 ...
public:
    typedef ListIterator<T, T&, T*> iterator;
    typedef ListIterator<T, const T&, const T*> const_iterator;

    // ... 省略其他代码 ...
};

为了实现一个反向迭代器,需要创建一个新的迭代器类,该类的增加(operator++)和减少(operator--操作符与标准迭代器的行为相反。也就是说,对于一个反向迭代器,operator++将会移动到前一个元素(_prev),而operator--将会移动到下一个元素(_next)。这意味着它将沿着相反的方向遍历列表。以下是如何定义一个ListIterator的反向版本的示例:

template<class T, class Ref, class Ptr>
struct ReverseListIterator
{
    typedef ListNode<T> Node;
    typedef ReverseListIterator<T, Ref, Ptr> Self;

    Node* _node;
    ReverseListIterator(Node* node)
        :_node(node)
    {}
    
    // 解引用操作符
    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& it)
    {
        return _node != it._node;
    }

    bool operator==(const Self& it)
    {
        return _node == it._node;
    }
};

这里的ReverseListIterator和原来的ListIterator很相似,区别就在于迭代器前进(++)和后退(--)的方向相反

通常情况下,标准库容器(比如listvector)会有.rbegin().rend()方法来获取反向迭代器的开始和结束。为了实现类似的功能,需要在容器类中添加生成ReverseListIterator实例的方法:

typedef ReverseListIterator<T, T&, T*> reverse_iterator;
typedef ReverseListIterator<T, const T&, constT*>const_reverse_iterator;

reverse_iterator rbegin()
{
	return reverse_iterator(end());
}

reverse_iterator rend()
{
	return reverse_iterator(begin());
}

2.适配实现反向迭代器

typedef ReverseListIterator<T, T&, T*> reverse_iterator;
typedef ReverseListIterator<T, const T&, constT*>const_reverse_iterator;

reverse_iterator rbegin()
{
	return reverse_iterator(end());
}

reverse_iterator rend()
{
	return reverse_iterator(begin());
}

这种实现只是对原有类的一个修改,只是对list这个反向迭代器的实现,我们下面来实现另一种适配模式,我传入某一容器的正向迭代器来适配生成反向迭代器

比如传入List类的正向迭代器,适配出List的反向迭代器,传入vector正向迭代器,适配出vector的反向迭代器

template<class Iterator, class Ref, class Ptr>
	struct ReverseIterator
	{
		typedef ReverseIterator<Iterator, Ref, Ptr> Self;
		Iterator _it;
		ReverseIterator(Iterator it)
			:_it(it)
		{}
		Ref operator*()
		{
			Iterator tmp = _it;
			return *(--tmp);
		}
		Ptr operator->()
		{
			//return _it->;
			//return _it.operator->();

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

在这个模板代码示例中,ReverseIterator 类型是一个反向迭代器,它是基于提供的正向迭代器类型 Iterator 来实现的当使用 ReverseIterator 时,编译器将会按照模板代码的描述来生成一个特定于所使用迭代器类型的类实例。以下是各个操作符和成员函数的作用,以及编译器如何处理它们:

1. 构造函数:

ReverseIterator(Iterator it) : _it(it) {}

构造函数接收一个正向迭代器 it 并存储在 _it 成员变量中。编译器处理构造函数的方式与普通的构造函数相同

2. 解引用操作符 operator*

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

这个操作符创建了 _it 的一个副本 tmp,然后对 tmp 应用前置自减 operator--,将其后移一位,然后解引用。对于反向迭代器而言,这意味着解引用的是当前位置之前的元素。

编译器生成了一个临时变量 tmp,调用了对应类型 Iterator 的前置自减 operator--,并调用了解引用 operator*。注意,由于这是一个底层实现细节,编译器有权优化这些步骤(如通过直接调用必要的函数)以优化性能

3. 成员访问操作符 operator->

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

这个操作符通过调用解引用操作符 operator* 来获取值的引用,然后取地址得到对应元素的指针

编译器会生成代码,使用上面定义的解引用操作符 operator* 来获取一个引用,然后获取该引用的地址

4. 前置自增操作符 operator++

Self& operator++()
{
    --_it;
    return *this;
}

对于反向迭代器来说,“自增”实际上会使内部正向迭代器 _it 自减。编译器调用 _it 的前置自减操作符 operator-- 并返回 *this 实现反向迭代器的自增操作

5. 前置自减操作符 operator--

Self& operator--()
{
    ++_it;
    return *this;
}

对于反向迭代器来说,“自减”实际上会使内部正向迭代器 _it 自增。编译器调用 _it 的前置自增操作符 operator++ 并返回 *this 实现反向迭代器的自减操作

6. 不等于操作符 operator!=

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

这个操作符比较两个反向迭代器,实际上它比较了内部正向迭代器 _it 是否不相等。

编译器根据 Iterator 类型生成了比较操作,这通常是调用 Iterator 给定的 operator!=

总结编译器处理:

本来每个容器都要写一个反向迭代器的累,但是自己写,太费劲了 本质写一个反向迭代器的类模板,给编译器传不同的容器的正向迭代器实例化,编译器帮助我们实例化出各种容器的对应反向迭代器

编写一个通用的反向迭代器类模板可以省去为每个容器单独定义反向迭代器的麻烦。C++ 标准库中的 std::reverse_iterator 就是这样一个通用的反向迭代器适配器。它接收一个正向迭代器作为模板参数,反转了其遍历方向,使得利用正向迭代器的容器可以很容易地提供反向迭代能力

使用类模板可以使得编译器根据你向模板传递的不同正向迭代器类型,为每个具体的容器类型生成对应的反向迭代器实例。这个通用反向迭代器适配器遵循了一种 编写一次,处处使用的原则,极大地提高了代码的复用性

例如,在 ReverseIterator 模板中,只要定义一次,就可以用来产生各种支持正向迭代器的容器的反向迭代器,如下所示:

std::vector<int> vec = {1, 2, 3, 4, 5};
ReverseIterator<std::vector<int>::iterator, int&, int*> rIt(vec.end());

// 使用反向迭代器遍历 vector
for (; rIt != ReverseIterator<std::vector<int>::iterator, int&, int*>(vec.begin()); ++rIt) {
    std::cout << *rIt << " ";
}

在这段代码中,ReverseIteratorstd::vector<int>::iterator 类型进行了实例化。实际上,因为 std::reverse_iterator 已经存在于标准库中,通常不需要自己写这个,并且可以直接这样使用

std::vector<int>::reverse_iterator rIt = vec.rbegin();
// 标准库已经提供 begin 和 end 了,不需要我们手动构建 ReverseIterator 实例
for (; rIt != vec.rend(); ++rIt) {
    std::cout << *rIt << " ";
}

本篇文章到此结束!!感谢大家阅读!!

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

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

相关文章

基于STC12C5A60S2系列1T 8051单片机的Proteus中的单片机发送一帧或一串数据给串口调试助手软件接收区显示出来的串口通信应用

基于STC12C5A60S2系列1T 8051单片机的Proteus中的单片机发送一帧或一串数据给串口调试助手软件接收区显示出来的串口通信应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机串口通信介绍STC12C5A60S2系列1T 8051单片机串口通信的结构基于STC12C5A60S2系列…

2024五一数学建模竞赛(五一赛)选题建议+初步分析

提示&#xff1a;DS C君认为的难度&#xff1a;B>A>C&#xff0c;开放度&#xff1a;AB<C。 以下为A-C题选题建议及初步分析&#xff1a; A题&#xff1a;钢板最优切割路径问题 l 难度评估&#xff1a;中等难度。涉及数学建模和优化算法&#xff0c;需要设计最优的…

windows驱动开发-电源管理

驱动程序收到的电源IRP的主功能码是IRP_MJ_POWER 以及四个次要代码: IRP_MN_POWER_SEQUENCE 驱动程序将此 IRP 作为优化发送&#xff0c;以确定其设备是否实际进入了特定的电源状态。 对此 IRP 的支持是可选的。 若要发送此 IRP&#xff0c;驱动程序必须调用 IoAllocateIrp …

鸿蒙准备1

鸿蒙心路 感慨索性&#xff0c; 看看鸿蒙吧。打开官网相关介绍 新建工程目录结构 感慨 最近面试Android应用开发&#xff0c;动不动就问framework的知识&#xff0c;什么touch事件的触发源是啥&#xff08;eventHub&#xff09;&#xff0c;gc流程是啥&#xff0c;图形框架是什…

SpringCloudStream 3.x rabbit 使用

1. 前言 今天带来的是SpringCloudStream 3.x 的新玩法&#xff0c;通过四大函数式接口的方式进行数据的发送和监听。本文将通过 rabbitMQ 的方式进行演示 3.x版本后是 可以看到 StreamListener 和 EnableBinding 都打上了Deprecated 注解。后续的版本更新中会逐渐替换成函数式…

修改Ubuntu远程登录欢迎提示信息

无论何时登录公司的某些生产系统&#xff0c;你都会看到一些登录消息、警告或关于你已登录服务器的信息&#xff0c;如下所示。 修改方式 1.打开ubuntu终端,进入到/etc/update-motd.d目录下面 可以发现目录中的文件都是shell脚本, 用户登录时服务器会自动加载这个目录中的文件…

深度学习之基于Matlab NN的伦敦房价预测

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 房价预测是房地产领域的一个重要问题&#xff0c;对于投资者、开发商以及政策制定者等都具有重要的指…

【写作吗。月入过万,8年写作路,10w+订阅创作者的18条建议】

Tom Kuegler &#xff1a;62k粉丝的medium独立作者&#xff0c;全网累计10w人订阅。他不是高深莫测&#xff0c;有资源背景的大V&#xff0c;也不是有运营团队的流量博主&#xff0c;而是从零开始&#xff0c;坚持不断写作的最普通创作者之一。 通过写作&#xff0c;他收入过万…

SpirngBoot整合快递100

目录 一、注册快递100 二、技术文档地址 三、需要认证的key和comcumer 四、spring boot 整合快递 100使用 4.1 引入快递100和hutool的依赖 4.2 将key和comcumer写入application.properties文件中 4.3 新建一个modle,用于将查出来的json数据转成对象 4.4 新建一个controll…

【yolov8】yolov8剪枝训练流程

yolov8剪枝训练流程 流程&#xff1a; 约束剪枝微调 一、正常训练 yolo train model./weights/yolov8s.pt datayolo_bvn.yaml epochs100 ampFalse projectprun nametrain二、约束训练 2.1 修改YOLOv8代码&#xff1a; ultralytics/yolo/engine/trainer.py 添加内容&#…

机器学习高频问答题总结

机器学习问答题总结 第一章 线性回归1.什么是线性回归&#xff1f;解释主要原理2.解释线性回归中最小二乘法的原理吗&#xff1f;3.如何评估线性回归模型的性能&#xff1f;4.线性回归中正则化的目的是什么吗&#xff1f;L1正则化和L2正则化有什么不同&#xff1f; 第二章 逻辑…

深入解析yolov5,为什么算法都是基于yolov5做改进的?(一)

YOLOv5简介 YOLOv5是一种单阶段目标检测算法&#xff0c;它在YOLOv4的基础上引入了多项改进&#xff0c;显著提升了检测的速度和精度。YOLOv5的设计哲学是简洁高效&#xff0c;它有四个版本&#xff1a;YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x&#xff0c;分别对应不同的模型大小…

神经网络与深度学习--网络优化与正则化

文章目录 前言一、网络优化1.1网络结构多样性1.2高维变量的非凸优化1.鞍点2.平坦最小值3.局部最小解的等价性 1.3.改善方法 二、优化算法2.1小批量梯度下降法&#xff08;Min-Batch&#xff09;2.2批量大小选择2.3学习率调整1.学习率衰减&#xff08;学习率退火&#xff09;分段…

MouseBoost PRO for Mac激活版:强大的 鼠标增强软件

在追求高效工作的今天&#xff0c;MouseBoost PRO for Mac成为了许多Mac用户的得力助手。这款功能强大的鼠标增强软件&#xff0c;以其独特的智能化功能和丰富的实用工具&#xff0c;让您的电脑操作更加便捷、高效。 MouseBoost PRO for Macv3.4.0中文激活版下载 MouseBoost PR…

nginxconfig.io项目nginx可视化配置--搭建-视频

项目地址 https://github.com/digitalocean/nginxconfig.io搭建视频 nginxconfig.io搭建 nginxconfig.io搭建 展示效果 找到这个项目需要的docker镜像&#xff0c;有项目需要的node的版本 docker pull node:20-alpine运行这个node容器,在主机中挂载一个文件夹到容器中 主机&a…

Python 与 TensorFlow2 生成式 AI(四)

原文&#xff1a;zh.annas-archive.org/md5/d06d282ea0d9c23c57f0ce31225acf76 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第九章&#xff1a;文本生成方法的崛起 在前几章中&#xff0c;我们讨论了不同的方法和技术来开发和训练生成模型。特别是在第六章“使用 …

LLM应用:让大模型prompt总结生成Mermaid流程图

生成内容、总结文章让大模型Mermaid流程图展示&#xff1a; mermaid 美人鱼, 是一个类似 markdown&#xff0c;用文本语法来描述文档图形(流程图、 时序图、甘特图)的工具&#xff0c;您可以在文档中嵌入一段 mermaid 文本来生成 SVG 形式的图形 Prompt 示例&#xff1a;用横向…

基于OSAL 实现UART、LED、ADC等基础示例 4

1 UART 实验目的 串口在我们开发单片机项目是很重要的&#xff0c;可以观察我们的代码运行情况&#xff0c;本节的目的就 是实现串口双工收发。 虽然说 osal 相关的代码已经跟硬件关系不大了&#xff0c;但是我们还是来贴出相关的硬件原理图贴出来。 1.1 串口初始化 osal_ini…

latex使用bib引用参考文献时,正文编号顺序乱序解决办法,两分钟搞定!

一、背景 用Latex写文章时&#xff0c;使用bib添加参考文献是一种最为简便的方式。但有的期刊模板&#xff0c;如机器人顶会IROS&#xff0c;会出现正文参考文献序号没按顺序排列的情况&#xff0c;如下图所示。按理说文献[4]应该是文献[2]&#xff0c;[2]应该是[3]&#xff0…

Go中为什么不建议用锁?

Go语言中是不建议用锁&#xff0c;而是用通道Channel来代替(不要通过共享内存来通信&#xff0c;而通过通信来共享内存)&#xff0c;当然锁也是可以用&#xff0c;锁是防止同一时刻多个goroutine操作同一个资源&#xff1b; GO语言中&#xff0c;要传递某个数据给另一个gorout…