分类: C/C++
2013-04-12 14:16:06
迭代器的作用其实相当于一个智能指针,它指向容器内部的数据,可以通过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为例说明迭代器的原理
// List节点的定义
template
struct __list_node {
typedef void* void_pointer;
void_pointer next;
void_pointer prev;
T data;
};
// List迭代器的定义
template
struct __list_iterator {
// 这三个typedef是为了简化后面的代码书写
typedef __list_iterator
typedef __list_iterator
typedef __list_iterator
typedef bidirectional_iterator_tag iterator_category; // 迭代器类型属于bidirectional iterator
typedef T value_type; // 值类型
typedef Ptr pointer; // 指针类型
typedef Ref reference; // 引用类型
typedef __list_node
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 list {
...
...
public:
typedef __list_iterator
typedef __list_iterator
...
...
protected:
link_type node; // 头节点,该List其实是一个带头节点的双向循环链表
public:
list() { empty_initialize(); }
iterator begin() { return (link_type)((*node).next); } // 返回头节点的下一个节点,即第一个节点的iterator
const_iterator begin() const { return (link_type)((*node).next); }
iterator end() { return node; } // 返回头节点的iterator,其实就是返回链表的结尾
const_iterator end() const { return node; }
...
...
}
如果我们对List容器使用find算法,这一过程中会发生什么?
int a[] = {1,2,3,4,5};
list
list
cout << *it << end;
先看看find函数的定义
template
InputIterator find(InputIterator first, InputIterator last, const T& value) {
while (first != last && *first != value) ++first;
return first;
}
我们所调用的find函数的特化版本其实是:
find<__list_iterator
从而find函数中所用到的!=、*、++等操作符都作用在__list_iterator
STL中迭代器的各种特性
还记得我在《STL源码剖析学习笔记2——神奇的__type_traits》中所提到的traits编程技巧么?在STL的迭代器中同样用到了这种技巧,因为STL的迭代器在使用的时候需要了解各种迭代器的特性。主要特性包含以下几种:
1、iterator_category:表示迭代器所属的类型
2、value_type:表示迭代器所指数据的类型
3、difference_type:表示两个迭代器之间的距离类型
4、pointer:表示迭代器所指数据的指针类型
5、reference:表示迭代器所指数据的引用类型
通常迭代器的几种特性被放在iterator_traits中。
// 对所有Iterator的泛化
template
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;
};
// 对指针类型的偏特化(Partial Spetialization)
template
struct iterator_traits
typedef random_access_iterator_tag iterator_category; // 指针类型是可以随机访问的
typedef T value_type; // 值类型
typedef ptrdiff_t difference_type; // 指针类型之间的距离一定是整型(ptrdiff_t被定义为int型)
typedef T* pointer;
typedef T& reference;
};
template
struct iterator_traits
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef const T* pointer;
typedef const T& reference;
};
各种不同的迭代器的特性定义如下:
// input iterator的属性
template
typedef input_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef T* pointer;
typedef T& reference;
};
// output iterator的属性
struct output_iterator {
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
};
// forward iterator的属性
template
typedef forward_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef T* pointer;
typedef T& reference;
};
// bidirectional iterator的属性
template
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef T* pointer;
typedef T& reference;
};
// random access iterator的属性
template
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
typename iterator_traits
count(InputIterator first, InputIterator last, const T& value) {
typename iterator_traits
for ( ; first != last; ++first)
if (*first == value)
++n;
return n;
}
eg2. advance模板函数,为了提高效率,必须针对不同类型的iterator重载不同的处理函数
template
inline void advance(InputIterator& i, Distance n) {
__advance(i, n, iterator_category(i)); // 根据不同的类型调用不同的重载函数
}
// iterator_category函数的定义
template
inline typename iterator_traits
iterator_category(const Iterator&) {
typedef typename iterator_traits
return category();
}
再看__advance函数针对不同迭代器的三种版本,它们分别针对input iterator、forward iterator、Bidirectional iterator和Random access iterator四种不同的迭代器
// 针对input iterator和forward iterator的版本
template
inline void __advance(InputIterator& i, Distance n, input_iterator_tag) {
while (n--) ++i; // 只能单向移动
}
// 针对Bidirectional iterator的版本
template
inline void __advance(BidirectionalIterator& i, Distance n,
bidirectional_iterator_tag) {
if (n >= 0) // 根据方向不同有不同的处理
while (n--) ++i;
else
while (n++) --i;
}
// 针对Random access iterator的版本
template
inline void __advance(RandomAccessIterator& i, Distance n,
random_access_iterator_tag) {
i += n; // 随机访问,提高效率
}
总的来说,在STL中是由容器(container)来负责设计适当的迭代器(iterator),由迭代器(iterator)来负责设计适当的迭代器
属性。正因为这一点才使得容器和算法可以完全分离开来,通过迭代器提供的接口来访问容器的内部元素。在这里我们又一次看到了traits编程技巧的强大功
能,在很大程度上弥补了C++语言不是强类型语言的不足之处。