文章目录

  • 前言
  • 一、list的反向迭代器
  • vector的反向迭代器
  • 总结

前言

反向迭代器的适配只用于双向迭代器,对于单链表实现的单向迭代器是不能通过适配构造一个反向迭代器的,为什么要说反向迭代器适配器呢?因为我们只需要实现一个反向迭代器模板就可以用所有的双向迭代器的正向实现其反向迭代器。


提示:以下是本篇文章正文内容,下面案例可供参考

一、list的反向迭代器

对于list而言,我们想要倒着遍历该如何实现呢?其实实现过正向迭代器的朋友们都知道,list的迭代器的++是指向链表的下一个节点,也就是node = node->next,那么要实现反向我们只需要将++实现为将node = node->prev即可,如下图所示:

    //反向迭代器template<class T, class Ref, class Ptr>struct list_reverse_iterator{typedef list_node<T> node;typedef list_reverse_iterator<T, Ref, Ptr> self;node* _node;list_reverse_iterator(node* n):_node(n){}Ref 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;}Ptr operator->(){return &_node->_data;}bool operator!=(const self& it){return _node != it._node;}bool operator==(const self& it){return _node == it._node;}};

可以看到我们上述代码对于反向迭代器确实只是将++变成了原先的--,--变成了原先的++。现在我们再写一下rbegin(),rbegin().

class list{public:typedef list_node<T> node;typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;typedef list_reverse_iterator<T, T&, T*> reverse_iterator;iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}reverse_iterator rbegin() {return reverse_iterator(_head->_prev);}reverse_iterator rend() {return reverse_iterator(_head);}

我们先将反向迭代器重命名为reverse_list,然后让rbegin()变成链表的尾节点,rend()还是链表的哨兵位头结点不变,下面我们来验证一下看是否正确:

通过验证我们发现确实可以实现反向迭代,那么现在问题来了,我们刚开始说了反向迭代器是一个适配器,如果我们用这个链表的反向迭代器去实现vector的反向迭代器可以实现吗?答案肯定是不行,因为vector又不是节点怎么返回节点的前一个后一个呢?所以我们实现的这个迭代器只能list使用,下面我们看看大佬是如何实现反向迭代器的:

我们圈出来的current是什么呢?current是一个正向迭代器

 那么为什么operator*变成了--迭代器然后解引用呢?再往下看:

反向迭代器的++是正向迭代器的--,反向迭代器的--是正向迭代器的++。

我们发现rbegin()是迭代器的end(),rend()是迭代器的begin(),下面我们画个图来验证一下:

这个时候我们终于理解了为什么operator*是--iterator的解引用,因为rbegin()是指向哨兵位的头结点的,这个时候是不能对头结点解引用的,所以我们应该对头结点的prev也就是最后一个节点解引用,这样一来就能依次从后往前访问到链表的每个元素,当rbegin()==rend()的时候将所有元素遍历完成。

下面我们就来实现一下:

namespace sxy
{template<class iterator,class Ref,class Ptr>struct ReverseIterator{typedef ReverseIterator<iterator, Ref, Ptr> self;iterator _cur;ReverseIterator(iterator it):_cur(it){}};
}

首先我们创建一个iterator.h文件,然后写一个模板这个模板第一个参数是任意类型的迭代器,第二个参数是迭代器中解引用的返回值Ref,第三个参数是在重载->符号的返回值Ptr。然后我们用正向迭代器创建一个变量_cur,构造函数初始化的时候直接用正向迭代器初始化_cur.

然后我们先实现++,--:

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

对于前置++和后置++的区别在于后置++要返回--前的那个值,所以我们直接用拷贝构造一个tmp,这里我们是没有实现迭代器的拷贝构造的,那么使用默认的拷贝构造可以吗?答案是可以,因为我们要的就是浅拷贝,我们就是要tmp指向cur位置,如下图:

我们再看一下深拷贝变成了什么:

通过上图我们应该理解了为什么我们直接使用默认的构造函数就能解决问题。

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

对于反向迭代器的判断我们只需要判断两个反向迭代器的节点是否相等即可。

下面我们实现反向迭代器的解引用:

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

对于反向迭代器的解引用我们说过,由于rbegin()是在end()的位置所以我们是不能直接解引用的,正确的操作是解引用此位置的前一个节点(为什么是前一个呢?因为是反向迭代器!)。所以我们用_cur拷贝构造一个tmp,然后--tmp就是前一个节点,然后再返回解引用的值即可。返回值的实现与正向迭代器一样具体可以去看我的list模拟实现那篇文章看看为什么要多一个模板参数Ref做返回值。

下面我们在list中定义一下反向迭代器:

首先包含一下头文件,然后typedef一下反向迭代器和反向迭代器的const版本:

typedef ReverseIterator<iterator,T&,T*> reverse_iterator;
typedef ReverseIterator<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());}

接下来我们看看是否能成功反向输出呢?

看来我们是成功了,那么回到我们一开始的问题,可以用这个迭代器去适配vector的反向迭代器吗?我们画个图看看:

 当反向迭代器++的时候是正向迭代器的--,然后从vector的尾部开始向头部移动。同时我们发现,operator*的实现也是满足的,因为rbegin()的位置是vector存放最后一个有效数据的位置的下一个,这个位置是没有数据的,只有让迭代器--一下在解引用才是正确的值,由于是每次位置的前一个所以当rbegin()==rend()的时候将反向遍历完所有数据,下面我们就演示一下vector的反向迭代器:

vector的反向迭代器

首先在vector.h的头文件中包含iterator.h的头文件:

然后在vector中typedef一下反向迭代器:

typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
typedef ReverseIterator<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());}

然后我们运行一下程序看是否能成功:

答案我们也看到了确实可以,这也就证明了我们一开始所说的大佬思想。当我们想实现list的反向迭代器的时候只实现出针对list的反向迭代器,而大佬却用一个反向迭代器适配其他的反向迭代器。


总结

对于迭代器的适配器最重要的是实现的思想,比如我们如何控制对迭代器进行解引用,++--等等,想要有如同大佬一般的思想我们必须多看优秀的代码,只有去看别人写的优秀的代码我们才能进步。

【c++】:反向迭代器适配器:每天学一点点优秀的思想相关推荐

  1. 【STL源码剖析】list模拟实现 | 适配器实现反向迭代器【超详细的底层算法解释】

    今天博主继续带来STL源码剖析专栏的第三篇博客了! 今天带来list的模拟实现! 话不多说,直接进入我们今天的内容! 前言 那么这里博主先安利一下一些干货满满的专栏啦! 手撕数据结构https://b ...

  2. 【C++】STL——反向迭代器的模拟实现:迭代器适配器

    文章目录 前言 1. list 的反向迭代器模拟实现 2. 思考 3. 库里面反向迭代器的实现--迭代器适配器 4. 反向迭代器模拟实现的改进--适配器模式 5. 适配器模式的实现--一劳永逸 6. ...

  3. 【C++】反向迭代器--迭代器适配器

    前言 大家好呀~欢迎进入我的这篇学习笔记~ 我的上一篇C++文章传送点在这里哦:[C++]stack.queue.priority_queue的模拟实现_柒海啦的博客-CSDN博客 我们知道,在C++ ...

  4. 反向迭代器---迭代器适配器

    文章目录 reverse_iterator认识 reverse_iterator 模拟实现 reverse_iterator在list内部的使用 reverse_iterator在vector内部的使 ...

  5. C++进阶——反向迭代器Reverse_iterator

    C++进阶--反向迭代器Reverse_iterator Reverse_iterator反向迭代器介绍 之前我们学过适配器,但是只实现了普通的迭代器,可是反向迭代器没有实现,当时说之后会实现的,没错 ...

  6. C++知识点29——使用C++标准库(迭代器适配器)

    在上一篇文章https://blog.csdn.net/Master_Cui/article/details/108512730谈到的迭代器是基本的五种类型的迭代器 但是随着C++标准库的扩展,又实现 ...

  7. 迭代器适配器{(插入迭代器back_insert_iterator)、IO流迭代器(istream_iterator、ostream_iterator)}...

    一.迭代器适配器 反向迭代器 插入迭代器 IO流迭代器 其中反向迭代器可以参考以前的文章. 二.插入迭代器 插入迭代器实际上是一个输出迭代器(*it=; ++) back_insert_iterato ...

  8. 【C++】迭代器、反向迭代器详解

    参考: http://c.biancheng.net/view/338.html https://blog.csdn.net/kjing/article/details/6936325 https:/ ...

  9. C++迭代器之'反向迭代器'

    反向迭代器(Reverse Iterator)是普通迭代器的适配器,通过重新定义自增和自减操作,以达到按反序遍历元素的目的.如果在标准算法库中用反向迭代器来代替普通的迭代器,那么运行结果与正常情况下相 ...

最新文章

  1. CENTOS下SAMBA服务不能开启的解决方法
  2. mongodb 3.2配置内存缓存大小为MB/MongoDB 3.x内存限制配置
  3. 文巾解题 leetcode1442. 形成两个异或相等数组的三元组数目
  4. SP1026 FAVDICE - Favorite Dice 期望dp
  5. github访问慢解决办法
  6. 异常将上下文初始化事件发送到类的侦听器实例_Java虚拟机从入门到入土之JVM的类加载机制
  7. w3school和w3cschool两个网站有什么关系和区别?
  8. 渝粤题库 陕西师范大学 《文字学概论》作业
  9. BeanUtils如何优雅的拷贝List
  10. c语言程序设计会出现什么问题,计算机C语言程序设计过程中的常见问题分析和研究...
  11. 3DES加密算法原理
  12. 【python】【数据处理】画多维数据分布图
  13. Eclipse反编译插件 Enhanced Class Decompiler
  14. Crackme001-Acid_burn(IDA版本)
  15. 用python告诉你,韦小宝跟他七个老婆哪个最亲?
  16. 关闭苹果无线服务器,苹果iOS11 WiFi、蓝牙无法关闭怎么回事?附彻底关闭方法...
  17. 为什么要使用MonoRail?
  18. 2、GO语言多进程编程
  19. 信奥一本通-动态规划-例9.2-数字金字塔-方法四-逆推法代码实现
  20. 2022年中国市场死亡的互联网产品

热门文章

  1. 游戏与App应用开发者收款结汇方式(涉及平台:Google Play、Apple)附平台规则!
  2. java实现视频和配音音频的合成
  3. Vue(组件间通信:props、自定义事件、全局事件总线、消息订阅与发布)
  4. 米兔积木机器人与履带机甲零件差别_米兔积木机器人履带机甲 给你一个智能的选择...
  5. 算法工程师每天都在干嘛?
  6. 史上最通俗易懂! 一文明白Git是干嘛的
  7. 《骆驼祥子》读书报告
  8. 招聘管理系统简单设计
  9. svga文件预览_工作案例分享:SVGA动效落地的使用与避坑
  10. Windows安装git客户端