本文分析使用的SGI版的STL。其可读性非常高,提供一个下载链接:
迭代器的种类
STL中有五种迭代器:输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机迭代器。
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 {};
在STL中,使用类的方式表示迭代器种类,使用继承表示迭代器之间的关系。例如随机迭代器一定是一种双向迭代器。
在定义容器迭代器的时候,需要定义它的类型。
template<class T, class Ref, class Ptr>
struct __list_iterator {
...
/*双向迭代器*/
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
...
};
template <class T, class Alloc = alloc>
class list {
...
public:
/*list容器迭代器*/
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
...
};
list容器内定义的迭代器是bidirectional_iterator_tag类型的,list在实现时,使用的是双向链表。所以它的迭代器可以前后移动。因为list不是连续空间,所以迭代器每次只能移动到相邻的结点,并不能进行跳跃。
struct __deque_iterator {
...
/*随机迭代器*/
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
...
};
template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque {
...
public:
/*deque容器迭代器*/
typedef __deque_iterator<T, T&, T*, BufSiz> iterator;
typedef __deque_iterator<T, const T&, const T&, BufSiz> const_iterator;
deque容器内的迭代器是random_access_iterator_tag类型的,在访问deque时,可以通过迭代器进行随机的访问deque内的元素。
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;
};
这就是迭代器萃取机,将迭代器丢入萃取机后,就可以获取迭代器相关的信息。
/*获取list<int>中的迭代器的类型为ListIterator*/
typedef list<int>::iterator ListIterator;
/*将ListIterator丢入萃取机,用于返回ListIterator的类型。*/
typedef iterator_traits<ListIterator>::iterator_category category;
返回的迭代器类型category,实际类型为bidirectional_iterator_tag。
template <class ListIterator>
struct iterator_traits {
typedef typename ListIterator::iterator_category iterator_category;
...
};
使用ListIterator实例化类模板后
/*核心是ListIterator::iterator_category*/
typedef typename ListIterator::iterator_category iterator_category;
ListIterator::iterator_category是核心内容
ListIterator::iterator_category
list<int>::iterator::iterator_category
list<int>::__list_iterator<int, int&, int*>::iterator_category
/*在__list_iterator内对iterator_category类型的定义*/
typedef bidirectional_iterator_tag iterator_category;
将ListIterator::iterator_category进一步展开,最后得到的结果就是__list_iterator<int, int&, int*>::iterator_category,这就是迭代器的类型。最后可知list<int>::iterator的类型是bidirectional_iterator_tag类型。
distance模板函数
distance函数用于计算两个迭代器之间的距离。
使用distance(itr1,itr2)计算vector和list迭代器之间的距离,虽然返回结果都是3,但是计算的方式是不同的。
vector内的迭代器是随机类型,计算itr1和itr2的距离,可以使用itr2-itr1直接得到。list的迭代器是双向类型的,不能直接相减,需要从itr1一直遍历到itr2,在遍历的时候,统计遍历元素的个数,就是两个迭代器之间的距离。
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last)
{
/*萃取迭代器的类型*/
typedef typename iterator_traits<InputIterator>::iterator_category category;
return __distance(first, last, category());
}
调用distance()函数,第一步先萃取处迭代器的类型,根据迭代器类型不同,进行不同方式的处理。
category就是迭代器的类型,category()是一个临时对象,这个临时对象主要的作用是,是在重载函数中找出匹配的重载函数。
/*迭代器是随机类型*/
template <class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type
__distance(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag)
{
return last - first; /*迭代器之间之间相减*/
}
如果category是RandomAccessIterator类型,则category()生成的临时对象就是RandomAccessIterator类型,那么就能匹配到这个重载函数。
随机类型的迭代器,计算迭代器之间的距离,仅仅是迭代器的相减。
__distance()函数中的random_access_iterator_tag参数,仅用于重载函数的匹配。
/*其他类型的迭代器*/
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
__distance(InputIterator first, InputIterator last, input_iterator_tag)
{
iterator_traits<InputIterator>::difference_type n = 0;
/*遍历*/
while (first != last) {
++first; ++n; /*n用于计数*/
}
return n;
}
如果category是bidirectional_iterator_tag、forward_iterator_tag、input_iterator_tag类型,则都会匹配到这个重载函数。虽然这个函数的参数的类型是input_iterator_tag,但bidirectional_iterator_tag、forward_iterator_tag是其子类,在没有完全匹配的重载函数时,也是可以匹配这个函数的。
这些类型的迭代器在计算距离时,需要从first迭代器一直遍历到last迭代器。
迭代器类型不同,分门别类的使用最适合迭代器的方法,可以提高性能。
对随机类型的迭代器使用遍历的方式计算距离,实属浪费。但其他类型的迭代器计算距离,只能使用遍历的方式。
萃取机的偏特化
vector的迭代器就是指针,并不像list、deque那样,是一个单独的类,可以在其内部定义迭代器的类型。
STL中通过iterator_traits偏特化,实现了对vector中纯指针类型迭代器的萃取。
/*偏特化 迭代器萃取机 */
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;
};
如果迭代器是一个单纯的指针,那么使用iterator_traits时,会使用这个偏特化版本。
单纯的指针是一种随机类型,可以随便跳跃。