C++ 反向迭代器

反向迭代器的++即正向迭代器的--,反向迭代器的--即正向迭代器的++,反向迭代器和正向迭代器的很多功能都是相似的,因此我们可以复用正向迭代器作为反向迭代器的底层容器来封装,从而实现出反向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装

这样一来我们可以实现任意容器的反向迭代器,如果用的是vector的正向迭代器,那么就封装出vector的反向迭代器,如果用的是list的正向迭代器,那么就封装出list的反向迭代器,这就是迭代器适配器了

反向迭代器:

template<class Iterator,class Ref,class Ptr>
class ReverseIterator
{typedef ReverseIterator<Iterator, Ref, Ptr> Self;
public:ReverseIterator(Iterator it):_it(it){}Self& operator++(){--_it;return *this;}Self operator++(int){Self tmp(*this);--_it;return tmp;}Self& operator--(){++_it;return *this;}Self operator--(int){Self tmp(*this);++_it;return tmp;}Ref operator*()//返回前一个位置的数据{Iterator cur = _it;return *(--cur);}Ptr operator->(){return &(operator*());}bool operator!=(const Self& s){return _it != s._it;}bool operator==(const Self& s){return _it == s._it;}private:Iterator _it;
};

库中设计的反向迭代器与正向迭代器是对称关系,关于解引用越界问题也很巧妙的处理了~:

解引用返回的是前一个位置的数据

 vector模拟实现:

#include<assert.h>
#include"Reverseiterator.h"
namespace djx
{template<class T>class vector{public: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;}vector()//一定要写,因为我们已经写了拷贝构造了,编译器不会生成默认的构造函数,当要使用无参的构造时如果我们没写,就没有,会报错{}vector(const vector<T>& v){reserve(v.capacity());for (auto& e : v){push_back(e);}}vector(size_t n, const T& val = T()){reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}vector<T>& operator=(vector<T> tmp){swap(tmp);return *this;}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}void push_back(const T& val){/*	if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = val;_finish++;*/insert(end(), val);//复用insert}void resize(size_t n, const T& val = T()){if (n <= size()){_finish = _start + n;}else{reserve(n);while (_finish < _start + n){*_finish = val;_finish++;}}}void insert(iterator pos, const T& val){assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = val;_finish++;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator begin = pos + 1;while (begin < _finish){*(begin - 1) = *begin;begin++;}_finish--;return pos;}size_t capacity() const{return _endofstorage - _start;}size_t size()const{return _finish - _start;}private:iterator _start = nullptr;//都会走构造,拷贝构造的初始化列表iterator _finish = nullptr;//给缺省值,就不用我们在构造,拷贝构造函数的初始化列表给值iterator _endofstorage = nullptr;};
}

测试vector的反向迭代器:

void func(const djx::vector<int>& v)
{djx::vector<int>::const_reverse_iterator rit = v.rbegin();while (rit != v.rend()){cout << *rit << " ";rit++;}cout << endl;
}int main()
{djx::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);djx::vector<int>::reverse_iterator rit = v.rbegin();while (rit != v.rend()){cout << *rit << " ";rit++;}cout << endl;func(v);return 0;
}

 

 

list模拟实现:

#include"Reverseiterator.h"
namespace djx
{template<class T>struct list_node{T _data;list_node<T>* _prev;list_node<T>* _next;list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};template<class T, class Ref, class Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self;Node* _node;__list_iterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}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& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};template<class T>class list{typedef list_node<T> Node;public: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(begin());}const_reverse_iterator rbegin()const{return const_reverse_iterator(end());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}iterator begin(){return _head->_next;}iterator end(){return _head;}const_iterator begin()const{return _head->_next;}const_iterator end()const{return _head;}void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}list(){empty_init();}list(const list<T>& lt){empty_init();for (auto e : lt){push_back(e);}}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=(list<T> lt){swap(lt);return *this;}~list(){clear();delete _head;_head = nullptr;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}void push_back(const T& x){insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;return newnode;}iterator erase(iterator pos){Node* cur = pos._node;Node* next = cur->_next;Node* prev = cur->_prev;delete cur;prev->_next = next;next->_prev = prev;_size--;return next;}size_t size(){return _size;}private:Node* _head;size_t _size;};
}

测试list的反向迭代器:

void func(const djx::list<int>& lt)
{djx::list<int>::const_reverse_iterator rit = lt.rbegin();while (rit != lt.rend()){cout << *rit << " ";rit++;}cout << endl;
}int main()
{djx::list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);djx::list<int>::reverse_iterator rit = lt.rbegin();while (rit != lt.rend()){cout << *rit << " ";rit++;}cout << endl;func(lt);return 0;
}

当然了,我们也可以按照不同于库中设计的逻辑来设计反向迭代器:但是存在一些细节需要处理

与库中反向迭代器设计模式不同之处在于解引用的设计 

上图版本的反向迭代器解引用重载的设计: 

	Ref operator*(){return *_it}

 那么,相应的在vector和list中也会发生变化:

vector中:

        reverse_iterator rbegin(){return reverse_iterator(end() - 1);}reverse_iterator rend(){return reverse_iterator(begin() - 1);}const_reverse_iterator rbegin() const{return const_reverse_iterator(end() - 1);}const_reverse_iterator rend() const{return const_reverse_iterator(begin() - 1);}

需要注意的是必须是end()-1 /begin()-1 而不能是--end()/--begin()

因为vector类中迭代器的实现,我们将其设计为原生指针T*,是内置类型

end()/begin() 为传值返回,返回的是临时对象,具有常性,不可被修改

list中:

        reverse_iterator rbegin(){return reverse_iterator(--end());}reverse_iterator rend(){return reverse_iterator(end());}const_reverse_iterator rbegin() const{return const_reverse_iterator(--end());}const_reverse_iterator rend() const{return const_reverse_iterator(end());}

也可以设计成end()-1,只不过list的正向迭代器是自定义类型,需要重载-运算符才可

那么问题就来了,为什么同样是各自的--end() ,vector就报错,我们知道因为返回的是临时对象具有常性导致的,但是list却不报错可以这样设计呢?

诚然,list的end()返回的也是临时对象,同样具有常性,不报错是因为这是特殊情况,特殊处理:具有常性的自定义类型的对象可以调用非const的函数

所以list中的--end()返回的自定义类型的临时对象可以调用list迭代器类中,非const的--运算符重载函数

如:

class A
{
public:A(int x = 0):_a(x){}void Print(){}private:int _a;
};

我们有时候会写出这样的代码:

A(1).Print(); // 特殊处理

A(1)是匿名对象具有常性,而print函数是非const的 

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

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

相关文章

【LeetCode 算法专题突破】双指针(⭐)

文章目录 前言1. 移动零题目描述代码 2. 复写零题目描述代码 3. 快乐数题目描述代码 4. 盛最多水的容器题目描述代码 5. 有效三角形的个数题目描述代码 6. 三数之和题目描述代码 7. 四数之和题目描述代码 总结 前言 学算法入门必学的一个章节&#xff0c;双指针算法&#xff0…

计算机网络-计算机网络体系结构-数据链路层

目录 *一、组帧 1.1字符计数法 1.2字符填充法 1.3零比特填充法 1.4违规编码 *二、差错控制 2.1检错编码 2.2.1奇偶校验码 2.2.2 CRC循环冗余码 2.2纠错编码-海明码 *三、流量控制和可靠传输机制 流量控制 停止-等待协议 ​编辑 后退n帧协议的滑动窗口(GBN) 选择…

ChatGPT AIGC 制作大屏可视化分析案例

第一部分提示词prompt: 商品 价格 p1 13 p2 41 p3 42 p4 53 p5 19 p6 28 p7 92 p8 62 城市 销量 北京 69 上海 13 南京 18 武汉 66 成都 70 你现在是一名非常专业的数据分析师,请结合上述数据完成下列几件事情 1:第一部分数…

基于 Triple 实现 Web 移动端后端全面打通

*作者&#xff1a;陈有为&#xff0c;陌陌研发工程师、Apache Dubbo PMC RPC 协议开发微服务 在我们正常开发微服务的时候&#xff0c;传统 RPC 服务可能在最底层。上层可能是浏览器、移动端、外界的服务器、自己的测试、curl 等等。我们可能会通过 Tomcat 这种外部服务器去组…

机器视觉知识讲的深不如讲的透

我深思这个话题&#xff0c;大家来培训&#xff0c;其实培训机构也很痛苦&#xff0c;每个热掌握的参差不齐&#xff0c;你说他不会吧&#xff0c;会一点电气&#xff0c;你说他会吧&#xff0c;会一点Opencv&#xff0c;会一点visionpro,会一点Visionmaster,会一点Halcon。好像…

【Retinex theory】【图像增强】-笔记

1 前言 retinex 是常见的图像增强的方法&#xff0c;retinex 是由两个单词合成的&#xff1a;retina conrtex &#xff0c;即视网膜皮层。 2 建立的基础 Land 的 retinex theory 建立在三个假设之下&#xff1a; 真实世界是无色的&#xff0c;我们所谓的颜色是光和物质相互…

上位机在自动化中有何作用和优势?

今日话题 上位机在自动化中有何作用和优势&#xff1f; 自动化控制编程领域包括单片机、PLC、机器视觉和运动控制等方向。输入“777”&#xff0c;即刻获取关于上位机开发和数据可视化的专业学习资料&#xff0c;近年来&#xff0c;上位机编程逐渐兴起&#xff0c;正在逐步替…

【Linux】环境下部署Nginx服务 - 二进制部署方式

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支…

Linux网络编程系列之服务器编程——非阻塞IO模型

Linux网络编程系列 &#xff08;够吃&#xff0c;管饱&#xff09; 1、Linux网络编程系列之网络编程基础 2、Linux网络编程系列之TCP协议编程 3、Linux网络编程系列之UDP协议编程 4、Linux网络编程系列之UDP广播 5、Linux网络编程系列之UDP组播 6、Linux网络编程系列之服务器编…

将license验证加入到系统中

1.将ClientDemo下的cn文件夹的内容导入项目对应的java目录下。 2.将license-config.properties文件导入resources目录下。 3.在项目的pom.xml中添加如下依赖。 <properties><!-- Apache HttpClient --><httpclient>4.5.5</httpclient><!-- License…

MySQL 4 MySQL使用演示(包含基本操作命令~~~)MySQL5.7编码设置

目录 1 MySQL的使用演示&#xff08;8.0和5.7版本对比&#xff09; 1、查看所有的数据库 2、创建自己的数据库 3、使用自己的数据库 4、查看某个库的所有表格 5、创建新的表格 6、查看一个表的数据 7、添加一条记录 8、查看表的创建信息 9、查看数据库的创建信息 …

关于Vue+webpack使用unocss编写CSS,打包后CSS没加前缀

关于Vuewebpack使用unocss编写CSS&#xff0c;打包后CSS没加前缀&#xff0c;封装了一个插件去解决了这个问题 unocss-postcss-webpack-plugin unocss在vite中使用配置&#xff0c;关于unocss在vite中使用&#xff0c;自行查阅官网 https://unocss.dev/integrations/vite ,vi…

【LeetCode】24. 两两交换链表中的节点

1 问题 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4] 输出&#xf…

Lua在计算时出现非法值,开启Debugger之后不再触发

1&#xff09;Lua在计算时出现非法值&#xff0c;开启Debugger之后不再触发 2&#xff09;从Gamma空间改为Linear空间会导致性能下降吗 3&#xff09;EXR格式在Unity中如何优化 4&#xff09;安卓游戏启动后提示“应用程序异常” 这是第355篇UWA技术知识分享的推送&#xff0c;…

基于VScode 使用plantUML 插件设计状态机

本文主要记录本人初次在VScode上使用PlantUML设计 本文只讲述操作的实际方法&#xff0c;假设java已安装成功 。 1. 在VScode下安装如下插件 2. 验证环境是否正常 新建一个文件夹并在目录下面新建文件test.plantuml 其内容如下所示: startuml hello world skinparam Style …

力扣 -- 1143. 最长公共子序列

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int longestCommonSubsequence(string s1, string s2) {int ms1.size();int ns2.size();s1 s1;s2 s2;vector<vector<int>> dp(m1,vector<int>(n1));for(int i1;i<m;i){for(int j1;j&…

【狂神说】HTML详解

目录 1 HTML概述1.1 什么是HTML1.2 HTML发展史1.3 HTML5的优势1.4 W3C标准 2 网页2.1 网页基本信息2.2 网页基本标签2.2.1 标题标签2.2.2 段落标签2.2.3 换行标签2.2.4 水平线标签2.2.5 字体样式标签&#xff1a;粗体、斜体2.2.6 注释和特殊符号 2.3 图像标签2.4 链接标签邮箱链…

C#串口原理

串口实际有2种。主要是电压逻辑不一样。玩单片机的人指usb转TTL的串口&#xff1b;普通人指USB转DB9的串口&#xff1b;先看下他们的区别&#xff1a; https://doc.embedfire.com/module/module_tutorial/zh/latest/Module_Manual/port_class/serial_port.html 1. 串口外设总结…

15.项目讲解之前端页面的实现

项目讲解之前端页面的实现 本项目前端使用HBuilerX软件编写HBuilderX下载安装配置一键直达&#xff0c; uniapp框架uniapp官网&#xff0c; 使用Element-ui组件Element-ui组件网址进行前端页面的完成。 前端项目下载地址 前端项目 前端项目展示 首页 首页展示 echarts实现…

简单的数学运算如何改变算法

简单的数学运算如何影响事物 当你坐在无人驾驶汽车上行驶时&#xff0c;突然发现前面有一个问题。一个亚马逊快递司机将他们的货车开到了一辆双停的UPS卡车旁边&#xff0c;然后才意识到无法通过。现在他们卡住了&#xff0c;你也卡住了。 街道太窄&#xff0c;无法实现U型转弯…