STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,彼此独立设计,最后再用迭代器(Iterator)把他们粘
合在一起。可见迭代器在STL中的重要程度。迭代器已经作为一种设计思想被记录与《设计模式》中,它的意图在于“提供一种方法顺序访问一个聚合对象中的各
个元素,而又不需暴露该对象的内部表示”。
迭代器的作用其实相当于一个智能指针,它指向容器内部的数据,可以通过operator *操作符来解指针获得数据的值,也可以通过operator
->操作符来获取数据的指针,还能够重载++,--等运算符来移动指针。
迭代器的分类
迭代器大致可以分为以下几种:
1、Input Interator :只允许作为输入,也就是只读(Read
Only)
2、Output Interator :只允许作为输出,也就是只写(Write Only)
3、Forward Interator
:允许读写,但只能做前向移动
4、Bidirectional Interator :允许读写,可以做双向移动
5、Random Access
Interator :允许读写,可以任意移动
- struct input_iterator_tag {};
- struct output_iterator_tag {};
- struct forward_iterator_tag : public input_iterator_tag {};
- struct bidirectional_iterator_tag : public forward_iterator_tag {};
- struct random_access_iterator_tag : public bidirectional_iterator_tag {};
实现原理
下面以List为例说明迭代器的原理
-
- template <class T>
- struct __list_node {
- typedef void* void_pointer;
- void_pointer next;
- void_pointer prev;
- T data;
- };
-
-
- template<class T, class Ref, class Ptr>
- struct __list_iterator {
-
- typedef __list_iterator<T, T&, T*> iterator;
- typedef __list_iterator<T, const T&, const T*> const_iterator;
- typedef __list_iterator<T, Ref, Ptr> self;
-
- typedef bidirectional_iterator_tag iterator_category;
- typedef T value_type;
- typedef Ptr pointer;
- typedef Ref reference;
- typedef __list_node<T>* link_type;
- typedef size_t size_type;
- typedef ptrdiff_t difference_type;
-
- link_type node;
-
-
- __list_iterator(link_type x) : node(x) {}
- __list_iterator() {}
- __list_iterator(const iterator& x) : node(x.node) {}
-
-
- bool operator==(const self& x) const { return node == x.node; }
- bool operator!=(const self& x) const { return node != x.node; }
-
-
- reference operator*() const { return (*node).data; }
-
- pointer operator->() const { return &(operator*());
-
- self& operator++() {
- node = (link_type)((*node).next);
- return *this;
- }
-
- self operator++(int) {
- self tmp = *this;
- ++*this;
- return tmp;
- }
-
- self& operator--() {
- node = (link_type)((*node).prev);
- return *this;
- }
-
- self operator--(int) {
- self tmp = *this;
- --*this;
- return tmp;
- }
- };
-
- 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;
-
- ...
- ...
-
- protected:
- link_type node;
-
- public:
- list() { empty_initialize(); }
-
- 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; }
-
- ...
- ...
- }
如果我们对List容器使用find算法,这一过程中会发生什么?
int a[] =
{1,2,3,4,5};
list<int> l(a, a+5);
list<int>::iterator it =
find(l.begin(), l.end(), 3);
cout << *it << end;
先看看find函数的定义
- template <class InputIterator, class T>
- InputIterator find(InputIterator first, InputIterator last, const T& value) {
- while (first != last && *first != value) ++first;
- return first;
- }
我们所调用的find函数的特化版本其实是:
find<__list_iterator<int, int&, int*>,
int>(__list_iterator<int, int&, int*> first,
__list_iterator<int, int&, int*> last, const int&
value)
从而find函数中所用到的!=、*、++等操作符都作用在__list_iterator<int, int&,
int*>的身上,这正是泛型的作用所在。
STL中迭代器的各种特性
还记得我在《STL源码剖析学习笔记2——神奇的__type_traits》中所提到的traits编程技巧么?在STL的迭代器中同样用到了这种技巧,因为STL的迭代器在使用的时候需要了解各种迭代器的特性。主要特性包含以下几种:
1、iterator_category:表示迭代器所属的类型
2、value_type:表示迭代器所指数据的类型
3、difference_type:表示两个迭代器之间的距离类型
4、pointer:表示迭代器所指数据的指针类型
5、reference:表示迭代器所指数据的引用类型
通常迭代器的几种特性被放在iterator_traits中。
-
- template <class Iterator>
- struct iterator_traits {
- typedef typename Iterator::iterator_category iterator_category;
- typedef typename Iterator::value_type value_type;
- typedef typename Iterator::difference_type difference_type;
- typedef typename Iterator::pointer pointer;
- typedef typename Iterator::reference reference;
- };
-
-
- template <class T>
- struct iterator_traits<T*> {
- typedef random_access_iterator_tag iterator_category;
- typedef T value_type;
- typedef ptrdiff_t difference_type;
- typedef T* pointer;
- typedef T& reference;
- };
- template <class T>
- struct iterator_traits<const T*> {
- typedef random_access_iterator_tag iterator_category;
- typedef T value_type;
- typedef ptrdiff_t difference_type;
- typedef const T* pointer;
- typedef const T& reference;
- };
各种不同的迭代器的特性定义如下:
-
- template <class T, class Distance> struct input_iterator {
- typedef input_iterator_tag iterator_category;
- typedef T value_type;
- typedef Distance difference_type;
- typedef T* pointer;
- typedef T& reference;
- };
-
- struct output_iterator {
- typedef output_iterator_tag iterator_category;
- typedef void value_type;
- typedef void difference_type;
- typedef void pointer;
- typedef void reference;
- };
-
- template <class T, class Distance> struct forward_iterator {
- typedef forward_iterator_tag iterator_category;
- typedef T value_type;
- typedef Distance difference_type;
- typedef T* pointer;
- typedef T& reference;
- };
-
- template <class T, class Distance> struct bidirectional_iterator {
- typedef bidirectional_iterator_tag iterator_category;
- typedef T value_type;
- typedef Distance difference_type;
- typedef T* pointer;
- typedef T& reference;
- };
-
- template <class T, class Distance> struct random_access_iterator {
- typedef random_access_iterator_tag iterator_category;
- typedef T value_type;
- typedef Distance difference_type;
- typedef T* pointer;
- typedef T& reference;
- };
通过iterator_traits就能得到相应interator的各种特性,这样可以让程序更灵活,也能提高效率。
下面几个例子是为了说明iterator_traits在STL中的使用
eg1.
count模板函数,它的返回值必须使用difference_type
- template <class InputIterator, class T>
- typename iterator_traits<InputIterator>::difference_type
- count(InputIterator first, InputIterator last, const T& value) {
- typename iterator_traits<InputIterator>::difference_type n = 0;
- for ( ; first != last; ++first)
- if (*first == value)
- ++n;
- return n;
- }
eg2. advance模板函数,为了提高效率,必须针对不同类型的iterator重载不同的处理函数
- template <class InputIterator, class Distance>
- inline void advance(InputIterator& i, Distance n) {
- __advance(i, n, iterator_category(i));
- }
-
-
- template <class Iterator>
- inline typename iterator_traits<Iterator>::iterator_category
- iterator_category(const Iterator&) {
- typedef typename iterator_traits<Iterator>::iterator_category category;
- return category();
- }
再看__advance函数针对不同迭代器的三种版本,它们分别针对input iterator、forward
iterator、Bidirectional iterator和Random access iterator四种不同的迭代器
-
- template <class InputIterator, class Distance>
- inline void __advance(InputIterator& i, Distance n, input_iterator_tag) {
- while (n--) ++i;
- }
-
- template <class BidirectionalIterator, class Distance>
- inline void __advance(BidirectionalIterator& i, Distance n,
- bidirectional_iterator_tag) {
- if (n >= 0)
- while (n--) ++i;
- else
- while (n++) --i;
- }
-
- template <class RandomAccessIterator, class Distance>
- inline void __advance(RandomAccessIterator& i, Distance n,
- random_access_iterator_tag) {
- i += n;
- }
总的来说,在STL中是由容器(container)来负责设计适当的迭代器(iterator),由迭代器(iterator)来负责设计适当的迭代器
属性。正因为这一点才使得容器和算法可以完全分离开来,通过迭代器提供的接口来访问容器的内部元素。在这里我们又一次看到了traits编程技巧的强大功
能,在很大程度上弥补了C++语言不是强类型语言的不足之处。