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

这篇具有很好参考价值的文章主要介绍了【c++】:反向迭代器适配器:每天学一点点优秀的思想。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

 文章来源地址https://www.toymoban.com/news/detail-407231.html

 

文章目录

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

 


前言

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


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

一、list的反向迭代器

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

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

 

    //反向迭代器
	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()还是链表的哨兵位头结点不变,下面我们来验证一下看是否正确:

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

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

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

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

【c++】:反向迭代器适配器:每天学一点点优秀的思想 那么为什么operator*变成了--迭代器然后解引用呢?再往下看:

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

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

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

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

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

 这个时候我们终于理解了为什么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位置,如下图:

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

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

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

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

        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中定义一下反向迭代器:

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

 首先包含一下头文件,然后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());
		}

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

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

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

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

vector的反向迭代器

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

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

然后在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());
		}

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

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

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

 


总结

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

 

 

到了这里,关于【c++】:反向迭代器适配器:每天学一点点优秀的思想的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 深入篇【C++】手搓模拟实现list类(详细剖析底层实现原理)&&模拟实现正反向迭代器【容器适配器模式】

    深入篇【C++】手搓模拟实现list类(详细剖析底层实现原理)&&模拟实现正反向迭代器【容器适配器模式】

    1.一个模板参数 在模拟实现list之前,我们要理解list中的迭代器是如何实现的。 在vector中迭代器可以看成一个指针,指向vector中的数据。它的解引用会访问到具体的数据本身,++会移动到下一个数据位置上去,这些都是因为vector具有天生的优势:空间上是连续的数组,这样指

    2024年02月15日
    浏览(12)
  • 面试之快速学习STL-迭代适配器

    面试之快速学习STL-迭代适配器

    参考:http://c.biancheng.net/view/7255.html 例子: 想使用反向迭代器实现逆序遍历容器,则该容器的迭代器类型必须是双向迭代器或者随机访问迭代器。 常见操作 注意这里不能用std::list,因为它是双向迭代器, +3的操作需要随机访问迭代器 。故联想到std::list排序只能使用list提供的

    2024年02月11日
    浏览(11)
  • C语言编程实现,计算每天进步一点点一年后的效果

    C语言编程实现,计算每天进步一点点一年后的效果

    本来的基数为1,如果好好学习时能力值相比前一天提高1%,当放任时相比前一天下降1%。1年(365天)后的效果相差多少呢? 原基数为1,努力一天进步1%,效果1*(1+0.01),努力两天是在前一天的基础上进步1%,结果是1*(1+0.01)*(1+0.01),一年后天天向上的力量是(1+0.01)的365次方。

    2024年02月11日
    浏览(13)
  • C++适配器模式

    1 简介: 适配器模式是一种结构型设计模式,用于将一个类的接口转换为客户端所期望的另一个接口。适配器模式允许不兼容的类能够协同工作,通过适配器类来实现接口的转换和适配。 2 实现步骤: 以下是使用C++实现适配器模式的步骤: a. 定义目标接口:首先,确定客户

    2024年02月12日
    浏览(7)
  • 适配器模式(C++)

    适配器模式(C++)

    将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象 ”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。 如何

    2024年02月14日
    浏览(6)
  • C++附加篇: 空间适配器

    C++附加篇: 空间适配器

     \\\"我有时难过,却还有些抚慰和感动。\\\"         STL的六大组件,容器、算法、迭代器、适配器、仿函数,最后一个也就是\\\"空间适配器\\\"。         所谓\\\"空间适配器\\\",顾名思义,就是对STL中各个容器的内存进行高效的管理。也许你会说,诶,我写了这么多的C++代码,为什

    2023年04月19日
    浏览(12)
  • C++ [STL容器适配器]

    C++ [STL容器适配器]

    本文已收录至《C++语言》专栏! 作者:ARMCSKGT 前面我们介绍了适配器模式中的反向迭代器,反向迭代器通过容器所支持的正向迭代器适配为具有反向迭代功能的迭代器,本节我们介绍STL中另一种适配器: 容器适配器 ! 前面我们提到过STL适配器模式,关于适配器的解释: S

    2024年02月11日
    浏览(7)
  • 【C++】手撕 栈 & 队列(适配器)

    【C++】手撕 栈 & 队列(适配器)

    目录 一,stack 1,stack的介绍 2,stack 框架 3,push(const T x) 4,pop() 5,top() 6,size() 7,empty() 8,stack 测试 9,源代码 二,queue 1,queue的介绍 2,queue 框架 3,push(const T x) 4,pop() 5,front() 6,back() 7,size() 8,empty() 9,queue 测试 10,源代码 三,总结 1,stack 是一种容器适配器,专门用

    2024年04月15日
    浏览(7)
  • C++之装饰器&适配器模式

    C++之装饰器&适配器模式

    目录 一、装饰器模式 模式思想 模式简介 模式优点 模式缺点 代码实现 情景模拟 代码实现 运行结果 二、适配器模式 模式简介 介绍 优点 缺点 代码实现 情景模拟 模式简介 装饰器模式( Decorator Pattern )允许向一个现有的对象 添加新的功能 ,同时又不改变其结构。 这种类型

    2024年02月13日
    浏览(15)
  • 【C++】STL 算法 ⑩ ( 函数适配器 | 函数适配器概念 | 函数适配器分类 | 函数适配器辅助函数 | std::bind2nd 函数原型及示例 | std::bind 函数原型及示例 )

    【C++】STL 算法 ⑩ ( 函数适配器 | 函数适配器概念 | 函数适配器分类 | 函数适配器辅助函数 | std::bind2nd 函数原型及示例 | std::bind 函数原型及示例 )

    在 STL 中 预定义了很多 函数对象 , 如果要 对 函数对象 的 参数 / 返回值 进行 计算 或 设置 , 可以 使用 \\\" 函数适配器 \\\" 实现上述需求 ; \\\" 函数适配器 \\\" 可以 将 已存在的 函数对象 转化为 另一种符合要求的 函数对象 ; \\\" 函数适配器 \\\" 定义在 functional 头文件 中 ; \\\" 函数适配器

    2024年02月02日
    浏览(21)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包