迭代器(Iterator)
前言
在STL的思想中,容器和算法是彼此独立设计的,再通过某种方式使它们连接;而迭代器是使算法独立于使用的容器类型,即迭代器是连接算法和容器的方法。由于迭代器是一种行为类似指针的对象,也就说迭代器是一种广义指针,即迭代器对解除引用操作(operator*)和访问成员操作(operator->)进行重载。然而要对这两个操作符进行重载,对容器内部对象的数据类型和存储结构有所了解,于是在 STL 中迭代器的最终实现都是由容器本身来实现的,每种容器都有自己的迭代器实现。本文介绍的不是针对某种特定的容器。
迭代器的分类
在SGI STL中根据读写和访问方式,在源码中迭代器大致可分为五类:
输入迭代器input_iterator: 只读,且只能一次读操作,支持操作:++p,p++,!=,==,=*p,p->;
输出迭代器output_iterator: 只写,且只能一次写操作,支持操作:++p,p++;
正向迭代器forward_iterator: 可多次读写,支持输入输出迭代器的所有操作;
双向迭代器bidirectional_iterator: 支持正向迭代器的所有操作,且支持操作:--p,--p;
随机访问迭代器random_access_iterator: 除了支持双向迭代器操作外,还支持:p[n],p+n,
n+p,p-n,p+=n,p-=n,p1-p2,p1<p2,p1>p2,p1>=p2,p1<=p2;
这五种迭代器的从属关系如下所示:
代码继承关系:
//五种迭代器,作为标记型别(tag types),不需要任何成员
struct input_iterator_tag{};
struct output_iteratoe_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{};
五种迭代器类型数据类型之一:
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;
};
... ...
迭代器的型别与traits技巧
在STL的算法设计中需要迭代器的型别,根据不同要求和不同型别,有不同的技巧解决;如:使用模板参数推导机制推导出局部变量类型;内嵌型别求出返回值类型;traits技术解决返回值类型和偏特化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;
}
通过Traits技术我们可以获得迭代器的相关类型的信息iterator_traits<…>::…,下面给出Traits技术的相关源码:
//为避免写代码时挂一漏万,自行开发的迭代器最好继承自下面这个 std::iterator
template<class Category,class T,class Distance=ptrdiff_t,class Pointer=T*,class Reference=T&>
struct iterator
{
typedef Category iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef Pointer pointer;
typedef Reference reference;
}
//针对原生指针(naive pointer)而设计的traits偏特性化版本
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;
};
//针对Pointer-to-const而设计的traits偏特化版本
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;
};
在迭代器中,为了能够在编译时确定函数调用,应用了函数重载。因为五种迭代器操作能力是不同的,例如random acess iterator是操作能力最强的,可以在O(1)时间操作指定位置,而这个用其他的迭代器可能需要O(n)。所以为了提高效率,使用迭代器类型最匹配的算法函数去调用:
1.首先通过traits技术获得迭代器类型iterator_category;
2.在函数调用时生成相应迭代器类型的临时对象作为实参传递,编译器就会调用相应的重载函数。
为了重载函数识别,SGI STL有对应的5种迭代器标识类:继承是为了可以使用传递调用,当不存在某种迭代器类型匹配时编译器会依据继承层次向上查找进行传递。
/*五中迭代器类型*/
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 {};
例如下面是advance()函数的调用,针对不同的迭代器类型,对函数进行重载:
/*函数重载,使迭代器能在编译时期就确定调用哪个函数*/