当前位置: 首页 > news >正文

stack_queue扩展学习 --- 反向迭代器

反向迭代器的实现思路

源码及框架分析

迭代器是用来遍历容器的,是一种封装,它不需要去关注容器的底层实现(底层是数组,链表,还是树等等这些结构),我们都是用统一的方式去对容器进行访问,访问行为是类似指针的。我们之前学习了普通迭代器和const迭代器:

  • 普通迭代器:能读能写;
  • const迭代器:只能读,只能遍历数据,得到数据,不能修改数据,是不能写的。

我们之前学的普通迭代器是正向迭代器,如果我想逆方向遍历呢?那么就需要反向迭代器。

SGI-STL30源代码,反向迭代器实现的核心源码在stl_iterator.h中,反向迭代器是一个适配器,各个容器中再适配出自己的反向迭代器,我们下面来看一看vector容器和list容器中的反向迭代器的核心部分:

在源码中,我们可以看到 reverse_iterator 实现了两个版本。通过 __STL_CLASS_PARTIAL_SPECIALIZATION 条件编译来控制使用哪个版本。简单来说,支持偏特化的迭代器萃取以后,反向迭代器使用的是以下版本:

template <class Iterator> 
class reverse_iterator;

而之前使用的是以下版本:

template <class BidirectionalIterator, class T, class Reference, class Distance> 
class reverse_bidirectional_iterator;template <class RandomAccessIterator, class T, class Reference, class Distance> 
class reverse_iterator;

可以看到,它们的主要差别在于模板参数是否传递迭代器指向的数据类型。支持偏特化的迭代器萃取以后,就不需要手动传递数据类型,因为 reverse_iterator 内部可以通过迭代器萃取获取数据类型。迭代器萃取的本质是一种特化,我们主要使用通过模板参数传递数据类型的方式来实现。

反向迭代器本质上是一个适配器,使用模板实现。传递哪个容器的迭代器,就可以封装适配出对应的反向迭代器。因为反向迭代器的功能与正向迭代器的功能高度相似,只是遍历方向相反,例如 operator++ 底层调用迭代器的 operator-- 等,所以通过封装就可以实现。

比较奇怪的是 operator* 的实现。它内部访问的是迭代器当前位置的前一个位置。这需要结合容器中 rbeginrend 的实现才能理解。rbegin 返回的是封装 end 位置的反向迭代器,rend 返回的是封装 begin 位置迭代器的反向迭代器。这里的设计是为了实现一种对称性,因此解引用访问的是当前位置的前一个位置。



反向迭代器的实现

反向迭代器通常是通过正向迭代器进行构造的。反向迭代器本质上是一个适配器,它对正向迭代器进行封装,从而实现反向遍历的功能。

反向迭代器的析构也不需要我们自己实现,因为迭代器不负责释放资源,释放资源是容器的事情!所以析构不需要写,就一般不需要写拷贝构造,因为不需要进行深拷贝!默认生成的浅拷贝就够用了! 

重点实现的是:

operator*operator->operator++operator--

operator* 就是要返回当前迭代器指向的数据,当前指向的位置是一个正向迭代器,上面的对象是包装了一个反向迭代器的。operator* 解引用,不管是正向还是反向,都要返回数据的引用! 

返回值类型设置 

对于反向迭代器来说,operator* 的行为确实需要特别注意。反向迭代器的目的是让遍历方向与正向迭代器相反,因此它的 operator* 需要返回当前迭代器所指位置的前一个元素。这是因为反向迭代器的逻辑起点是容器的末尾(end()),而不是开头(begin())。

  • 正向迭代器begin() 指向第一个元素,end() 指向最后一个元素的下一个位置。

  • 反向迭代器rbegin() 指向最后一个元素,rend() 指向第一个元素的前一个位置。

因此,反向迭代器的 operator* 实现为:

T& operator*() const { return *(current - 1); }

这里 current 是存储的正向迭代器,current - 1 表示前一个位置。

反向迭代器的模板参数

反向迭代器的模板参数设计取决于迭代器的类型和容器的特性。主要有以下两种情况:

a. 通用模板参数

对于通用的反向迭代器,模板参数通常包括:

  • Iterator:正向迭代器类型。

  • T:迭代器所指元素的类型。

  • Reference:引用类型(T&const T&)。

  • Distance:迭代器距离类型(如 std::ptrdiff_t)。

template <class Iterator, class T, class Reference, class Distance>
class reverse_iterator {
public:Reference operator*() const { return *(current - 1); }// 其他成员函数...
private:Iterator current;
};
b. 偏特化和类型萃取

对于某些容器(如 std::vector),其迭代器可能是原生指针,没有内嵌类型(如 referencevalue_type)。这种情况下,需要通过偏特化或类型萃取来获取正确的类型信息。

  • std::list 的迭代器:是一个自定义类型,可以通过 typename Iterator::reference 获取引用类型。

  • std::vector 的迭代器:是一个原生指针,没有内嵌类型,需要通过偏特化或类型萃取来获取正确的类型。

template <class Iterator>
class reverse_iterator {
public:// 使用类型萃取获取引用类型using reference = typename std::iterator_traits<Iterator>::reference;reference operator*() const { return *(current - 1); }// 其他成员函数...
private:Iterator current;
};

为什么需要偏特化或类型萃取

  • std::list 的迭代器:是一个类模板实例,具有内嵌类型(如 referencevalue_type)。因此,可以直接通过 typename Iterator::reference 获取引用类型。

  • std::vector 的迭代器:是一个原生指针(如 T*),没有内嵌类型。因此,需要通过偏特化或类型萃取来获取正确的类型。

template <class T>
class reverse_iterator<T*> {
public:using reference = T&;reference operator*() const { return *(current - 1); }// 其他成员函数...
private:T* current;
};
  • operator* 的行为:反向迭代器的 operator* 返回当前迭代器所指位置的前一个元素。

  • 模板参数设计:通用模板参数可以处理大多数情况,但对于原生指针类型的迭代器,需要通过偏特化或类型萃取来获取正确的类型信息。

  • 类型萃取:通过 std::iterator_traits 可以统一处理不同类型迭代器的特性,包括原生指针和自定义迭代器。

其实就是总结为:

如果是普通迭代器,那就适配出普通反向迭代器:T& operator*,如果是const迭代器,就适配出const反向迭代器:const T& operator*,只是template<class Iterator>的话,是不能够的,Iterator内部有没有该数据类型?list是有的,以为list的iterator是一个自定义类型,直接使用typename Iterator::reference,也就是取他的内嵌类型(取内嵌类型需要加typename),但是vector就是一个原生指针,没有内嵌类型的概念,这时候就需要偏特化的类型萃取才可以搞定!

其实我们这里就不要那么复杂了,使用三个模板参数就可以了!

template<class Iterator, class Ref, class Ptr>

下面,为我们就可以实现一个:

Iterator.h

#pragma once// 反向迭代器模板
template<class Iterator, class Ref, class Ptr>
struct ReverseIterator
{typedef ReverseIterator<Iterator, Ref, Ptr> Self; // 自引用类型别名,方便后续使用// 构造函数:接受一个正向迭代器ReverseIterator(Iterator it): _it(it) // 初始化内部存储的正向迭代器{}// 解引用操作符:返回当前迭代器前一个位置的引用Ref operator*(){// 适配的不一定是随机迭代器,可能是双向迭代器或输入迭代器// 不是双向迭代器又不能减,只可以进行减减// 而且不能直接对_it"--"解引用,因为这会改变_it的值// 创建一个临时迭代器,向前移动一位Iterator tmp = _it;--tmp; // 确保迭代器支持--操作// 返回前一个位置的引用return *tmp;}// 成员访问操作符:返回解引用的地址Ptr operator->(){return &(operator*()); // 复用 operator* 的实现}// 前置递增操作符:递减内部迭代器Self& operator++(){--_it; // 内部迭代器向前移动return *this; // 返回当前反向迭代器}// 前置递减操作符:递增内部迭代器Self& operator--(){++_it; // 内部迭代器向后移动return *this; // 返回当前反向迭代器}// 不等于操作符:比较两个反向迭代器是否不相等bool operator!=(const Self& s){return _it != s._it; // 比较内部迭代器}// 等于操作符:比较两个反向迭代器是否相等bool operator==(const Self& s){return _it == s._it; // 比较内部迭代器}Iterator _it; // 内部存储的正向迭代器
};

我们就可以在List.h中进行反向迭代器的使用了:(结合上面图片进行代码的编写)

		typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;typedef ReverseIterator<iterator, T&, T*> reverse_iterator;typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;//reverse_iterator rbegin()//{//	return reverse_iterator(--end());//}//reverse_iterator rend()//{//	return reverse_iterator(end());//}// 对称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());}

 我们就可以在Vector.h中进行反向迭代器的使用了:

typedef T* iterator;
typedef const T* const_iterator;typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
typedef ReverseIterator<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());}iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}

附录

迭代器类别支持的操作示例
输入迭代器 (Input Iterator)单向遍历,支持 ++*->std::istream_iterator
输出迭代器 (Output Iterator)单向写入,支持 ++*std::ostream_iterator
前向迭代器 (Forward Iterator)双向遍历,支持 ++*->std::forward_list::iterator
双向迭代器 (Bidirectional Iterator)双向遍历,支持 ++--*->std::list::iteratorstd::map::iterator
随机访问迭代器 (Random Access Iterator)随机访问,支持 ++--+-[]*->std::vector::iteratorstd::deque::iterator

本片的全部正文:

反向迭代器https://gitee.com/small-entrepreneur/c-additional-meal

http://www.dtcms.com/a/270126.html

相关文章:

  • 戴尔3670装win11和ubuntu双系统踩坑教程
  • 自动驾驶传感器的标定与数据融合
  • 【Android】组件及布局介绍
  • CAN主站转Modbus TCP网关:高铁门控系统的“毫秒级响应”密码
  • 【ZYNQ Linux开发】BRAM的几种驱动方式
  • 微服务集成snail-job分布式定时任务系统实践
  • Mac安装Docker(使用orbstack代替)
  • 单机分布式一体化数据库的架构设计与优化
  • 一个猜想不等式的推广
  • 业务分析技术实践篇
  • kafka集群安装
  • 让事情变得更好
  • Shader面试题100道之(21-40)
  • 光流 | RAFT光流算法如何改进提升
  • 【适合 Java 工程师的 AI 转型方向】
  • 基于PHP/MySQL的企业培训考试系统源码,高并发、稳定运行,源码开源可二开
  • Java中的生产消费模型解析
  • Distance Information Improves Heterogeneous Graph Neural Networks
  • 质量小议56 - 说教
  • [ESP32]VSCODE+ESP-IDF环境搭建及blink例程尝试(win10 win11均配置成功)
  • vscode打开stm32CubeIDE的项目的注释问题
  • 从分层训练到一步生成:Kaiming He 的生成模型进化之路—CVPR2025演讲小结
  • 网络--初级
  • springboot单体项目的发布生产优化
  • DMA(直接内存访问)是什么?
  • 第2章,[标签 Win32] :匈牙利标记法
  • 13届蓝桥杯省赛程序设计试题
  • 字符串大小比较的方式|函数的多返回值
  • 作业03-SparkSQL开发
  • 数字化校园升级:传统网络架构与SD-WAN智能方案对比详解