C++加餐课-stack_queue:反向迭代器

1. 源码及框架分析

SGI-STL30版本源代码,反向迭代器实现的核心源码在stl_iterator.h中。

反向迭代器是一个适配器,各个容器中再适配出自己的反向迭代器。

下面我们截出vector和list的的反向迭代器结构框架核心部分截取出来如下:

cpp 复制代码
// stl_list.h
template <class T, class Alloc = alloc>
class list {
public:
 typedef __list_iterator<T, T&, T*> iterator;
 typedef __list_iterator<T, const T&, const T*> const_iterator;
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
 typedef reverse_iterator<const_iterator> const_reverse_iterator;
 typedef reverse_iterator<iterator> reverse_iterator;
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
 typedef reverse_bidirectional_iterator<const_iterator, value_type,
 const_reference, difference_type> const_reverse_iterator;
 typedef reverse_bidirectional_iterator<iterator, value_type, reference,
 difference_type> reverse_iterator; 
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */
 iterator begin() { return (link_type)((*node).next); }
 const_iterator begin() const { return (link_type)((*node).next); }
 iterator end() { return node; }
 const_iterator end() const { return node; }
 
 reverse_iterator rbegin() { return reverse_iterator(end()); }
 const_reverse_iterator rbegin() const { return
const_reverse_iterator(end());}
 reverse_iterator rend() { return reverse_iterator(begin()); }
 const_reverse_iterator rend() const { return
const_reverse_iterator(begin());}
};
cpp 复制代码
// stl_vector.h
template <class T, class Alloc = alloc>
class vector {
public:
 typedef T value_type;
 typedef value_type* iterator;
 
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
 typedef reverse_iterator<const_iterator> const_reverse_iterator;
 typedef reverse_iterator<iterator> reverse_iterator;
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
 typedef reverse_iterator<const_iterator, value_type, const_reference, 
 difference_type> const_reverse_iterator;
 typedef reverse_iterator<iterator, value_type, reference, difference_type>
 reverse_iterator;
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */
 iterator begin() { return start; }
 const_iterator begin() const { return start; }
 iterator end() { return finish; }
 const_iterator end() const { return finish; }
 
 reverse_iterator rbegin() { return reverse_iterator(end()); }
 const_reverse_iterator rbegin() const { return
const_reverse_iterator(end());}
 reverse_iterator rend() { return reverse_iterator(begin()); }
 const_reverse_iterator rend() const { return
const_reverse_iterator(begin());}
};
cpp 复制代码
// stl_iterator.h
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
// This is the new version of reverse_iterator, as defined in the
// draft C++ standard. It relies on the iterator_traits template,
// which in turn relies on partial specialization. The class
// reverse_bidirectional_iterator is no longer part of the draft
// standard, but it is retained for backward compatibility.
template <class Iterator>
class reverse_iterator
{
protected:
	Iterator current;
public:
	typedef typename iterator_traits<Iterator>::iterator_category
		iterator_category;
	typedef typename iterator_traits<Iterator>::value_type
		value_type;
	typedef typename iterator_traits<Iterator>::difference_type
		difference_type;
	typedef typename iterator_traits<Iterator>::pointer
		pointer;
	typedef typename iterator_traits<Iterator>::reference
		reference;
	typedef Iterator iterator_type;
	typedef reverse_iterator<Iterator> self;
public:
	reverse_iterator() {}
	explicit reverse_iterator(iterator_type x) : current(x) {}
	reverse_iterator(const self& x) : current(x.current) {}
#ifdef __STL_MEMBER_TEMPLATES
	template <class Iter>
	reverse_iterator(const reverse_iterator<Iter>& x) : current(x.current) {}
#endif /* __STL_MEMBER_TEMPLATES */

	iterator_type base() const { return current; }
	reference operator*() const {
		Iterator tmp = current;
		return *--tmp;
	}
#ifndef __SGI_STL_NO_ARROW_OPERATOR
	pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
	self& operator++() {
		--current;
		return *this;
	}
	self operator++(int) {
		self tmp = *this;
		--current;
		return tmp;
	}
	self& operator--() {
		++current;
		return *this;
	}
	self operator--(int) {
		self tmp = *this;
		++current;
		return tmp;
	}
	self operator+(difference_type n) const {
		return self(current - n);
	}
	self& operator+=(difference_type n) {
		current -= n;
		return *this;
	}
	self operator-(difference_type n) const {
		return self(current + n);
	}
	self& operator-=(difference_type n) {
		current += n;
		return *this;
	}
	reference operator[](difference_type n) const { return *(*this + n); }
};
template <class Iterator>
inline bool operator==(const reverse_iterator<Iterator>& x,
	const reverse_iterator<Iterator>& y) {
	return x.base() == y.base();
}
template <class Iterator>
inline bool operator<(const reverse_iterator<Iterator>& x,
	const reverse_iterator<Iterator>& y) {
	return y.base() < x.base();
}
template <class Iterator>
inline typename reverse_iterator<Iterator>::difference_type
operator-(const reverse_iterator<Iterator>& x,
	const reverse_iterator<Iterator>& y) {
	return y.base() - x.base();
}
template <class Iterator>
inline reverse_iterator<Iterator>
operator+(reverse_iterator<Iterator>::difference_type n,
	const reverse_iterator<Iterator>& x) {
	return reverse_iterator<Iterator>(x.base() - n);
}
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
// This is the old version of reverse_iterator, as found in the original
// HP STL. It does not use partial specialization.
template <class BidirectionalIterator, class T, class Reference = T&,
	class Distance = ptrdiff_t>
class reverse_bidirectional_iterator {
	typedef reverse_bidirectional_iterator<BidirectionalIterator, T, Reference,
		Distance> self;
protected:
	BidirectionalIterator current;
public:
	typedef bidirectional_iterator_tag iterator_category;
	typedef T value_type;
	typedef Distance difference_type;
	typedef T* pointer;
	typedef Reference reference;
	reverse_bidirectional_iterator() {}
	explicit reverse_bidirectional_iterator(BidirectionalIterator x)
		: current(x) {
	}
	BidirectionalIterator base() const { return current; }
	Reference operator*() const {
		BidirectionalIterator tmp = current;
		return *--tmp;
	}
#ifndef __SGI_STL_NO_ARROW_OPERATOR
	pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
	self& operator++() {
		--current;
		return *this;
	}
	self operator++(int) {
		self tmp = *this;
		--current;
		return tmp;
	}
	self& operator--() {
		++current;
		return *this;
	}
	self operator--(int) {
		self tmp = *this;
		++current;
		return tmp;
	}
};
template <class RandomAccessIterator, class T, class Reference = T&,
	class Distance = ptrdiff_t>
class reverse_iterator {
	typedef reverse_iterator<RandomAccessIterator, T, Reference, Distance>
		self;
protected:
	RandomAccessIterator current;
public:
	typedef random_access_iterator_tag iterator_category;
	typedef T value_type;
	typedef Distance difference_type;
	typedef T* pointer;
	typedef Reference reference;
	reverse_iterator() {}
	explicit reverse_iterator(RandomAccessIterator x) : current(x) {}
	RandomAccessIterator base() const { return current; }
	Reference operator*() const { return *(current - 1); }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
	pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
	self& operator++() {
		--current;
		return *this;
	}
	self operator++(int) {
		self tmp = *this;
		--current;
		return tmp;
	}
	self& operator--() {
		++current;
		return *this;
	}
	self operator--(int) {
		self tmp = *this;
		++current;
		return tmp;
	}
	self operator+(Distance n) const {
		return self(current - n);
	}
	self& operator+=(Distance n) {
		current -= n;
		return *this;
	}
	self operator-(Distance n) const {
		return self(current + n);
	}
	self& operator-=(Distance n) {
		current += n;
		return *this;
	}
	Reference operator[](Distance n) const { return *(*this + n); }
};
#endif //__STL_CLASS_PARTIAL_SPECIALIZATION

• 源码中我们可以看到reverse_iterator实现了两个版本,通过**__STL_CLASS_PARTIAL_SPECIALIZATION** 条件编译控制使用哪个版本,简单点说就是支持 偏特化的迭代器萃取以后,反向迭代器使用的是这个版本,**template <class Iterator> class reverse_iterator;**之前使用的是

template <class BidirectionalIterator, class T, class Reference, class Distance>
class reverse_bidirectional_iterator;

template <class RandomAccessIterator, class T, class Reference ,class Distance>
class reverse_iterator;

• 可以看到他们的差别主要是在模板参数是否传递迭代器指向的数据类型,支持偏特化的迭代器萃取以后就不需要给了,因为reverse_iterator内部可以通过迭代器萃取获取数据类型。迭代器萃取的本质是⼀个特化,这个还有一些有点儿复杂且不影响我们学习主线内容,这里我们就不讲解了,有兴趣且基础功底好的同学想了解可以去看源码,有问题再讨论。这个我们主要使用模版参数传递数据类型的方式实现。

• 反向迭代器本质是一个适配器,使用模版实现,传递哪个容器的迭代器就可以封装适配出对应的反向迭代器。因为反向迭代器的功能跟正向的迭代器功能高度相似,只是遍历的方向相反,类似operator++ 底层调用迭代器的operator-- 等,所以封装一下就可以实现。

• 比较奇怪的是operator*的实现,内部访问的是迭代器当前位置的前一个位置。这个要结合容器中 rbegin和rend实现才能看懂,rbegin返回的是封装end位置的反向迭代器,rend返回的是封装begin位置迭代器的反向迭代器,这里是为了实现出一个对称,所以解引用访问的是当前位置的前一个位置。

2. 实现反向迭代器

cpp 复制代码
// ReverseIterator.h
// 所有容器的反向迭代器
// 迭代器适配器
namespace bit
{
	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 &(operator*());
		}
		Self& operator++()
		{
			--_it;
			return *this;
		}
		Self& operator--()
		{
			++_it;
			return *this;
		}
		Self operator++(int)
		{
			Self tmp(*this);
			--_it;
			return tmp;
		}
		Self operator--(int)
		{
			Self tmp(*this);
			--_it;
			return tmp;
		}
		bool operator!=(const Self& s) const
		{
			return _it != s._it;
		}
		bool operator==(const Self& s) const
		{
			return _it != s._it;
		}
	};
}
cpp 复制代码
// vector.h
#include"ReverseIterator.h"
namespace bit
{
	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;
		}
		// ....
	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _endofstorage = nullptr;
	};
}
cpp 复制代码
// list.h
#include"ReverseIterator.h"
namespace bit
{
	template<class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<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;
		}
		// ...
	private:
		Node* _head;
		size_t _size;
	};
}
cpp 复制代码
// test.cpp
#include"list.h"
#include"vector.h"
int main()
{
	bit::list<int> lt = { 1,2,3,4 };
	bit::list<int>::reverse_iterator rit = lt.rbegin();
	while (rit != lt.rend())
	{
		//*rit = 1;
		cout << *rit << " ";
		++rit;
	}
	cout << endl;

	return 0;
}
//int main()
//{
// bit::vector<int> v = { 1,2,3,4 };
// bit::vector<int>::reverse_iterator rit = v.rbegin();
// while (rit != v.rend())
// {
// //*rit = 1;
// cout << *rit << " ";
// ++rit;
// }
// cout << endl;
//
// return 0;
//}

3. 课堂演示

回顾:迭代器是一种封装,用来遍历容器,不需要关注容器的底层结构,都使用统一的方式进行容器的访问,迭代器的访问行为类似于指针。

之前学习的普通的迭代器------能读能写,和const迭代器------只读。

反向迭代器能实现容器的倒着遍历。


学完栈和队列之后,了解了适配器的概念,再来学反向迭代器更好。

反向迭代器是一个适配器,各个容器中再适配出自己的反向迭代器。

3.1 源码分析

vector的反向迭代器:

list的反向迭代器:

都是一个单独的类,定义在stl.iterator里面的一个类。


可以看到stl_list.h和stl_vector.h都没有包含这个头文件,但是可以使用这个类型。

其实stl_list.h是包含在<list>里面的,在<list>里面包含stl_list.h之前的其他头文件中包含了stl.iterator,头文件展开之后,就能找到了。


在stl_vector.h和stl_list.h里面:

  • 定义了__STL_CLASS_PARTIAL_SPECIALIZATION这个宏,则反向迭代器只有一个模版参数。
  • 没定义__STL_CLASS_PARTIAL_SPECIALIZATION这个宏,则反向迭代器有四个模版参数。

在stl_iterator里面:

  • 一个模版参数的版本

  • 三个模版参数的版本

__STL_LIMITED_DEFAULT_TEMPLATES是限制默认参数,即是否支持默认参数,这也是一个条件编译。

用这份源码的老一点的编译器可能不支持默认参数。

整体而言这里一共有三个反向迭代器的类模版。


这3个版本的反向迭代器的功能类似,都是适配器模式。

反向迭代器和正向迭代器的行为相似、方向相反。针对每个容器单独去写反向迭代器没必要,STL觉得能复用尽量就复用。

于是就计划用每个容器自己的正向迭代器,封装适配出反向迭代器。

封装了之后去实现反向迭代器的各种接口:

  • operator*、operator->:直接复用正向迭代器的;
  • operator++:复用正向迭代器的operator--;
  • operator--:复用正向迭代器的++;

一个模版参数的迭代器:是针对任意类型的迭代器------支持双向迭代器、随机迭代器。

它重载了operator++、operator--、operator+(任意值)、operator-(任意值)。

迭代器从功能上划分:

  • 单向迭代器(++);
  • 双向迭代器(++、--);
  • 随机迭代器(++、--、+、-、+=、-=、[])

反向双向迭代器就是专门针对双向迭代器,只重载了++、--。

早期只有这两个迭代器,链表就是使用反向双向迭代器来封装迭代器。

现在这个版本的反向迭代器类模版已经不使用了。


后期都是希望使用一个模版参数的迭代器来统一封装------有点问题,需要依赖iterator_traits。

iterator_traits:迭代器萃取。****(本质是一种特化)

早期没有迭代器萃取这个类型,就提供了4个模版参数的迭代器。


3.2 代码实现

有这个宏的时候,是一个模版参数的反向迭代器。

没有这个宏的时候,是两种4个模版参数的反向迭代器。

PARTIAL_SPECIALIZATION:偏特化。

定义了这个宏,支持迭代器萃取,就可以使用一个模版参数的反向迭代器版本。


3.2.1 成员变量、默认成员函数

3.2.2 成员函数

【operator*、operator->】

由于涉及到返回T&还是const T&、T*还是const T*,所以这里增加两个模版参数。

如果想保持只用一个模版参数,也是可以的。

因为正向迭代器这个类型里面有T&、const T&、T*、const T*。

声明类域取类型就可以了(加typename)

但是这种方法对于vector不适用,因为vector里面没有reference类型,它的迭代器就是原生指针,没有内嵌类型。

这时候就需要借助------偏特化的类型萃取。


这里简单一点实现,就实现成3个模版参数。

【operator++、operator--】
【operator!=、operator==】

3.3 list封装出反向迭代器

分析:

代码:


【测试】


【差异】

源码里面的operator*和上面的实现方式不一样,不是直接返回*it。

源码返回不是当前位置,而是前一个位置。

这里确定是随机迭代器,可以使用operator-。

这里是先赋值给tmp,再对tmp--,因为单模版参数的版本不确定是双向迭代器还是随机迭代器。

current的指向不变,operator*返回的是current的前一个位置。


库里面这样设计,是为了保持rbegin、rend的代码对称性。

在内层特殊设计operator*,是为了在外层某个容器封装反向迭代器的时候可以对称设计。

更加方便、统一。

相当于是下面这种结构:

那遍历的时候访问到的第一个结点是哨兵位就坑了。

所以修改了解引用的逻辑:不访问当前结点,访问当前结点的前一个结点。


【修改】

【测试】

3.4 vector封装出反向迭代器

【测试】

相关推荐
云栖梦泽1 小时前
Linux内核与驱动:12.设备树实例分析
linux·c++·单片机
计算机安禾2 小时前
【数据结构与算法】第45篇:跳跃表(Skip List)
c语言·数据结构·算法·list·排序算法·图论·visual studio
papership2 小时前
【入门级-算法-4、算法策略:差分】
数据结构·算法
代码改善世界2 小时前
【C++初阶】stack和queue用法详解:常用接口、模拟实现与面试题(附完整代码)
开发语言·c++
承渊政道2 小时前
【递归、搜索与回溯算法】(递归问题拆解与经典模型实战大秘笈)
数据结构·c++·学习·算法·macos·dfs·bfs
少司府2 小时前
C++基础入门:类和对象(下)
开发语言·c++·类型转换·类和对象·友元
tankeven2 小时前
动态规划专题(05):区间动态规划实践(乘法游戏)
c++·算法·动态规划
im_AMBER2 小时前
Leetcode 156 旋转图像 | 矩阵置零
javascript·数据结构·算法·leetcode