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

慈云数据 8个月前 (03-12) 技术支持 111 0

 

 

文章目录

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

     


    前言

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


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

    一、list的反向迭代器

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

    66cf8c8f3d59410896177cf4a949f75f.png

     

        //反向迭代器
    	template
    	struct list_reverse_iterator
    	{
    		typedef list_node node;
    		typedef list_reverse_iterator 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 node;
    		typedef list_iterator iterator;
    		typedef list_iterator const_iterator;
    		typedef list_reverse_iterator 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()还是链表的哨兵位头结点不变,下面我们来验证一下看是否正确:

    802e04367fc24aaead6aed185b7773e9.png

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

    ba9e44366e2d42e4b116da10076cdc9f.png

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

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

    b138c3385ebb423b8bd36d3ac503bfa0.png

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

    b09ac5ed92df49af9150ece018941a81.png

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

    b8df1d1105f04f3386c6f4ec297cbd36.png

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

    下面我们就来实现一下:

    namespace sxy
    {
    	template
    	struct ReverseIterator
    	{
    		typedef ReverseIterator 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位置,如下图:

    58e39d68f0b64d6888514444a8655832.png

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

    d1d7f75c777a44bf9636dea271a47f86.png

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

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

    a3c5649e3be34331a62ee5251e77d54b.png

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

    typedef ReverseIterator reverse_iterator;
    typedef ReverseIterator 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());
    		}

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

    709f83d6a15b47e0b636a2f2b4c20036.png

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

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

    vector的反向迭代器

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

    3d935443c2094165b45a7bcc32f1ce5d.png

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

    typedef ReverseIterator reverse_iterator;
    typedef ReverseIterator 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());
    		}

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

    f2de274672404c5faf7e5c9649c601ed.png

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

     


    总结

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

     

     

微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon