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

devtools/2024/10/18 16:48:58/

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;}// ++itSelf& 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.ppmy.cn/devtools/28567.html

相关文章

Springboot之文件操作记录存储服务

概述 应公司安全管理部门政策要求,需要实现文件上传/下载操作的日志记录,经过分析需要在目前平台上基于springboot搭建一套服务供其他应用具体业务调用,其中该服务涉及到的技术支撑&#xff1a;AOP实现异常处理、queuespring-scheduler异步执行定时任务、Fegin组件进行服务间通…

Quartz.Net技术教学:构建高效的任务调度系统

Quartz.Net技术教学&#xff1a;构建高效的任务调度系统 对于定时任务、后台数据处理等相信也是大家经常遇到的需求啦。为了满足这些需求&#xff0c;Quartz.Net作为一款功能强大的任务调度框架&#xff0c;受到了广大开发者的青睐。本文就从Quartz.Net的基本概念、核心组件、…

图像识别的突破:使用MNIST数据集训练你的首个深度学习模型

引言 在深度学习的世界里&#xff0c;MNIST数据集相当于是“Hello World”程序。它包含了大量的手写数字图像&#xff0c;是初学者学习图像识别和训练神经网络的理想起点。在这篇博客中&#xff0c;我们将结合NVIDIA深度学习DLI基础课程的内容&#xff0c;学习如何使用MNIST数…

【Scala---01】Scala『 Scala简介 | 函数式编程简介 | Scala VS Java | 安装与部署』

文章目录 1. Scala简介2. 函数式编程简介3. Scala VS Java4. 安装与部署 1. Scala简介 Scala是由于Spark的流行而兴起的。Scala是高级语言&#xff0c;Scala底层使用的是Java&#xff0c;可以看做是对Java的进一步封装&#xff0c;更加简洁&#xff0c;代码量是Java的一半。 因…

openlayer 使用ol-ext插件实现凸显区域

使用ol-ext插件实现凸显多变形 效果如图 1、创建openlayer var map; var view; var tileLayer, source, vector;function init() {tileLayer new ol.layer.Tile({source: new ol.source.TileArcGISRest({url: "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStr…

Xcode15安装iOS17模拟器及显示iOS真机

前言 升级完Xcode15之后&#xff0c;本地模拟器 Simulator 全被清空&#xff0c;真机也不显示&#xff08;&#x1f634;惑&#xff09;&#xff0c;编译按钮也无法点击&#xff0c;只有一个选项&#xff08;如下图所示&#xff09;。 点击下面的管理设备&#xff0c;可以显示…

使用 Go 语言统计 0-200000 的数字中,哪些是素数?

题目 使用 Go 语言统计 0-200000的数字中&#xff0c;哪些是素数&#xff1f; 思路 两种方法&#xff1a; 单循环遍历 1-200000 数字&#xff0c;并判断是否是素数。 使用了 Goroutine 和通道实现并发&#xff1a; 通过创建两个通道 intChan 和 primeChan&#xff0c;以及一…

Redux数据流架构

Redux的难点是理解它对于数据修改的规则, 下图动态展示了在整个数据的修改中&#xff0c;数据的流向 Redux代码被分为三个核心的概念&#xff0c;三个概念分别是: state: 一个对象 存放着我们管理的数据action: 一个对象 用来描述你想怎么改数据reducer: 一个函数 根据action的…