STL — 迭代器设计思维(二)

来源:互联网 发布:补牙 知乎 编辑:程序博客网 时间:2024/05/17 01:38

迭代器设计思维(二)




上一篇博客主要介绍了STL迭代器中的5种类型的迭代器,以及迭代器当中的5种相应型别,最后提到了traits编程技法. 这些都是尤为重要的,我们阅

源码最重要的就是理解加应用. 今天我们继续往下走,对于const迭代器比较容易就是将模板类型变为const T. 大多数迭代器的设计接口都是可以容

普通迭代器接口也可以容纳const迭代器接口. 我们今天来看看STL_iterator中的反向迭代器是如何定义的,它的代码复用性相当的高.

提到反向迭代器呢,我还是首先提到配接器吧。 配接器在STL组件的灵活组合运用功能上,扮演着轴承,转换器的角色. Adapter这个概念,事实上是

种设计模式. 《Design patterns》一书提到23个最普及的设计模式,其中对adapter样式的定义如下: 将一个class的接口转换为另一个class的接

口,使原本因接口不兼容而不能合作的classes,可以一起运作.


STL所提供的两个容器queue和stack,其实都只不过是一种配接器. 他们修饰deque的接口而成就出另一种容器风貌. STL所提供的各种配接器中,改变

仿函数接口者,我们称为function addapter,改变容器接口者,我们称为container adapter,改变迭代器接口者,我们称之为iterator adapter.


STL提供了许多应用于迭代器身上的配接器,包括insert iterators,reverse iterator,iostream iterator. C++ standard规定他们的接口可以藉由

<iterator>获得,SGI STL则将他们实际定义于<stl_iterator.h>

而我们现在就需要着重来注意这个Reverse Iterator,所谓reverse Iterator,可以将一般迭代器的行进方向逆转,是原本应该前进的operator++变成

后退操作,使原本应该后退的operator--变成前进的操作. 这种错乱的行为不是为了掩人耳目或为了欺敌效果,而是因为这种倒转筋的性质运用在"从

尾端开始进行"的算法上,有很大的方便性.



Reverse Iterator



没有例外,只要双向序列容器提供了begin(),end(),他们的rbegin(),rend()就是下面的样式. 单向序列容器如slist不可使用reserve iterators,

更有的容器如stack,queue,priority_queue并不提供begin(),end(),当然也没有rbegin(),rend().

// 摘自 SGI STL 3.0 STL_vectortemplate <class T, class Alloc = alloc> //空间配置器class vector {public:typedef T value_type;typedef value_type* iterator;typedef reverse_iterator<iterator> reverse_iterator;reverse_iterator rbegin() { return reverse_iterator(end()); }reverse_iterator rend() { return reverse_iterator(begin()); }...}// 摘自 SGI STL 3.0 STL_dequetemplate <class T, class Alloc = alloc, size_t BufSiz = 0>class deque {public:                         // Basic typestypedef T value_type;typedef __deque_iterator<T, T&, T*, BufSiz> iterator;typedef reverse_iterator<iterator> reverse_iterator;iterator begin() { return start; }iterator end() { return finish; }reverse_iterator rbegin() { return reverse_iterator(finish); }reverse_iterator rend() { return reverse_iterator(start); }...}// 摘自 SGI STL 3.0 STL_Listtemplate <class T, class Alloc = alloc>class list {protected:typedef __list_iterator<T, T&, T*> iterator;typedef reverse_iterator<iterator> reverse_iterator;reverse_iterator rbegin() { return reverse_iterator(end()); }reverse_iterator rend() { return reverse_iterator(begin()); }...}

我们再看看下面这个代码:

#include<vector>#include<iostream>using namespace std;int main(){int arr[] = { 1, 2, 3, 4, 5, 6, 7 };vector<int> arr2(arr,arr+7);vector<int>::iterator it1 = find(arr2.begin(), arr2.end(), 6); //正向迭代器指向6的这个位置reverse_iterator<vector<int>::iterator> it2(it1); //让逆向迭代器也指向6这个位置cout << *it1 << endl;cout << *it2 << endl;system("pause");return 0;}

运行结果:



为什么我们的"正向迭代器"和"与其指向同样位置的反向迭代器"会输出不同的元素呢? 这并不是一个潜伏的错误,而是一个刻意为之的特性,主要是

为了配合迭代器区间的"前闭后开"的习惯. 我们下面这个图的rebegin()和end()的关系可以看出,当迭代器被逆转方向时,虽然其实体位置不变,但是

其逻辑地址(迭代器指向的值)改变了.



唯有这样,才能保持正向迭代器的一切惯常行为,换句话说,唯有这样,当我们将一个正向迭代器区间转换为一个逆向迭代器区间后,不必再有任何

额外处理,就可以让接受这个逆向迭代器区间的算法,以相反的元素次序来处理区间中的每一个元素. 例如:

copy(arr2.begin(), arr2.end(), outite);//1 2 3 4 5 6 7copy(arr2.rbegin(), arr2.rend(), outite);//7 6 5 4 3 2 1

那么我们来看看源代码中,是如何来实现reverse_iterator的我们就会有一个大致的了解啦:

template <class Iterator>class reverse_iterator{protected:Iterator current; //记录对应之正向迭代器.public://迭代器的5种相应型别都和其对应的正向迭代器相同.typedef typename iterator_traits<Iterator>::iterator_categoryiterator_category;typedef typename iterator_traits<Iterator>::value_typevalue_type;typedef typename iterator_traits<Iterator>::difference_typedifference_type;typedef typename iterator_traits<Iterator>::pointerpointer;typedef typename iterator_traits<Iterator>::referencereference;typedef Iterator iterator_type; //代表正向迭代器typedef reverse_iterator<Iterator> self;//代表逆向迭代器public:reverse_iterator() {}//下面这个ctor将reverse_iterator与某个迭代器系结起来explicit reverse_iterator(iterator_type x) : current(x) {}reverse_iterator(const self& x) : current(x.current) {}#ifdef __STL_MEMBER_TEMPLATEStemplate <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_OPERATORpointer operator->() const { return &(operator*()); } //对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;}//注意,下面第一个* 和 唯一一个 + 都会调用本类的operator* 和 operator+//第二个 * 则不会(判断法则:完全看处理的型别是什么而定)reference operator[](difference_type n) const { return *(*this + n); }};