1、迭代器的基本原理与功能
经过前面两文章的铺垫,现在终于要开始讲迭代器的具体实现了。可以先看一下迭代器的常见用法:
vector<int> vec;
for(int i=0;i<5;i++)//将元素0~4装进容器
vec.push_back(i);
vector<int>::iterator iter=vec.begin();//获取迭代器
for(int i=0;i<4;i++)
{
cout<<*iter<<endl;//逐个输出元素0~4
iter++;
}
//accumulate(iterator start,iterator end, value_type value)
//将value作为初始值,将[start,end)范围内的元素累加起来
accumulate(vec.begin(),vec.end(),0); //返回结果为0+0+1+2+3+4 (第一个0为初始值)
从上面熟悉的用法可以看出,迭代器就好像一个指针,它指向某种类型的元素,并且可以通过运算获取其指向的元素的值。从accumulate中迭代器的用法可以看出,只要给定了首尾两个迭代器,那么就可以将这两个迭代器之间的元素累加起来。为了达到这一目的,我们可以猜测背后迭代器要实现的两个功能如下:
**1能够通过迭代器取出迭代器指向的元素的值
**2能够以某种顺序遍历首尾迭代器之间的所有元素
像上面的图展示的那样,我们平常熟悉的指针其实就是一种迭代器,通过对指针解引用就可以得到对应元素的值。这种情况下,指针就是地址,指针通过自增(减)运算可以向移动。如果一开始指针指向起点(begin),那么指针每自增一次,就向右移动一个元素,这样不断移动直到指向end位置,就可以遍历所有的元素。
虽然普通指针可以看成时迭代器,显然迭代器不仅仅是指针!迭代器像指针一样具有两个最基本的特性(功能):
(1)通过运算可以得到迭代器指向元素的值
(2)通过++(或者–)可以移动迭代器指向元素的位置
当迭代器就是普通指针(数组的地址)时,运算就是指针的解引用,而++运算移动到下一个地址,这些都不需要我们自己去实现。
事实上,迭代器可以比指针更具有一般性。只要可以通过实现解引用、通过++移动迭代器遍历元素等功能,都可以看做是指针,如下图所示。但是当迭代器不是普通的地址时,就需要我们自己通过重载运算符*、++等去实现相应功能。
事实上迭代器实现的最基本的运算就是上面两个,迭代器的设计也都是围绕这两个基本功能展开的。
2、为什么要设计迭代器
从上面已经看到迭代器具有遍历元素的功能,同时迭代器既然是一种类似指针的东西,因此还可以通过迭代器修改相关元素的值。
要设计迭代器,首先要确定要针对的数据结构。比如,针对vector、list都设计了迭代器。因为一个迭代器一个重要的功能就是遍历元素,因此,要设计迭代器的数据结构一般是包含多个元素的集合(要是只有一个元素,就不需要遍历这么麻烦了,直接取值就ok)
3、动手设计一个迭代器
要设计迭代器之前需要确定是针对何种数据结构设计迭代器,为此,我们不妨先设计一个链表。(因为链表有多个节点,这样我们就可以通过迭代器进行遍历操作了)
//先设计链表的节点
template<typename T>
class ListNode
{
public:
ListNode(const T t) : Data(t), Next(NULL) {}
void setnext(ListNode<T>* n)
{
Next = n;
}
ListNode<T>* next()
{
return Next;
}
T data(){return Data;}
private:
ListNode<T>* Next;
T Data;
};
//链表
template<typename T>
class list
{
public:
list():Head(NULL) {}
list(ListNode<T>*pitem):Head(pitem){}
void push(const T& t)
{
ListNode<T>* Data = new ListNode<T>(t);
Data->setnext(Head);
Head = Data;
}
ListNode<T>* front()
{
return Head;
}
private:
ListNode<T>* Head;
};
//针对链表设计的迭代器
template <typename T>
class ListIter
{
public:
typedef T value_type;
typedef T& reference ;
typedef T* pointer;
ListIter(pointer p = NULL) :Iter(p) {}
//迭代器基本功能,后置++ /前置++/取值*
ListIter& operator++()//前置自增运算
{
Iter = Iter->next();
return *this;
}
ListIter operator++(int)//后置自增运算
{
ListIter tmp = *this;
++(*this);
return tmp;
}
reference operator*() //解引用,即获取迭代器指向的元素
{
return *Iter;
}
private:
pointer Iter;
};
//test如下:
int main()
{
list<string> listDemo;
listDemo.push(string("this"));
listDemo.push("is");
listDemo.push("a");
listDemo.push("test");
listDemo.push("example");
ListIter<ListNode<string> > iter(listDemo.front());//获取迭代器
ListIter<ListNode<string> > end;
for(int i=0;i<3;i++)
{
cout << (*iter).data() <<endl;
iter++;
}
return 0;
}
显示:
this
is
a
test
example
4、STL迭代器的实现
相信通过上面的学习,大家对迭代器的基本原理和实现已经差不多清楚了。写到我,我总感觉对不起标题:毕竟标题是STL迭代器的实现原理,好像写到这个地方,还没怎么提到STL中的迭代器。好吧,还是简单说一说吧(这部分知识建议大家对着源码看一下,需要用到的基础知识都差不多铺垫好了)。
(1)STL中的迭代器一步步到底是怎么实现的
相信很多人和我一样,学习这块内容的时候,老是在想标准容器(比如vector)的迭代器时怎么一步步实现的。
比如Algorithm.h定义了一个函数advance(iterator ,distance)函数,该函数可以求出迭代器移动近距离distance后的值。
vector<int> vec;
for(int i = 0;i < 10;i++)
vec.push_back(i);
vector<int>::iterator it = vec.begin();//迭代器的声明与赋值
//advance使用
std::cout<<"After advance 3:";
advance(it,3);
std::cout << *it << " "<<std::endl;//输出结果为3
STL源码实现如下:
template<class T, class Alloc = allocator<T>>
class vector{
private:
T *start_;
public:
typedef T* iterator;
……//其它省略
}
iterator begin(){ return (start_);
}
从上面可以看到,当用int实例化vector容器模板时,里面的类型T=int,而嵌套定义iterator类型为T*,所以
vector<int>::iterator it等价与 int *it
而begin()返回start_,这是一个T*型,即int*指针,返回容器元素的起始地址。
advance函数相关定义如下:
template<class InputIterator, class Distance>//输入迭代器类型
void _advance(InputIterator& it, Distance n, input_iterator_tag){
assert(n >= 0);
while (n--){
++it;
}
}
template<class BidirectionIterator, class Distance>//双向迭代器类型
void _advance(BidirectionIterator& it, Distance n, bidirectional_iterator_tag){
if (n < 0){
while (n++){
--it;
}
}else{
while (n--){
++it;
}
}
}
template<class RandomIterator, class Distance>//随机迭代器类型
void _advance(RandomIterator& it, Distance n, random_access_iterator_tag){
if (n < 0){
it -= (-n);
}else{
it += n;
}
}
template <class InputIterator, class Distance>
void advance(InputIterator& it, Distance n){
typedef typename iterator_traits<InputIterator>::iterator_category iterator_category;
_advance(it, n, iterator_category());
}
从上面可以看到advance函数实际上是调用_advance()函数,并且_advance()函数通过第三个参数进行了重载,那么这里是调用的哪一个呢?显然取决于_advance(it, n, iterator_category())中iterator_category()类型。
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;
};
由于我们调用函数:advance(it,3),it为int*型指针。因此
typedef typename iterator_traits<InputIterator>::iterator_category iterator_category;
InputIterator为int*,此时对应iterator_traits模板的指针偏特化版本iterator_traits<T*>
该版本中iterator_category实际上为random_access_iterator_tag类型,故advance(it,3)最终调用的是
_advance(it,3,input_iterator_tag()),
(2)STL中不同的迭代器类型有啥区别以及该怎么使用
看STL源码可以发现里面有下面几种迭代器:
不同的迭代器具有不同的读写功能。我们可以根据这些迭代器来设计我们自己的迭代器。设计的思路就是,让我们自己的迭代器继承上面的迭代器。
//设计一个输入型迭代器,只需要继承input_iterator即可
template <typename T,typename Distance>
class myInputIterator:public input_iterator<T,Distance>//一定要public继承
{
//其它省略
}
定义好之后,就可以开始使用了:
假如 myInputIt 为myInputIterator<int,int> 类型迭代器,那么my继承了input_iterator类型的iterator_category, 并且iterator_category为input_iterator_tag类型
那么在提取类型的时候
typedef typename iterator_traits<InputIterator>::iterator_category iterator_category
相当于:
typedef typename iterator_traits<myInputIt>::iterator_category iterator_category
即:
typedef input_iterator_tag iterator_category
一个具体的例子:
比如advance(it,3)最终调用的是_advance(it,3,random_access_iterator_tag()),那么我们能不能设计一个迭代器,使advance(it,3)最终调用的是_advance(it,3,input_iterator_tag())。显然是可以的,只要让it迭代器类型为input_iterator即可。
//先设计一个input_iterator类型迭代器
class my_input_iterator:public input_iterator<int,int> //继承基本类型
{
public:
my_input_iterator(int *p):pint(p){}//构造函数
my_input_iterator &operator++()//重载自增运算符(前置)
{
pint++;//指针自增
return *this;//返回该迭代器
}
int operator*()//重载*运算符
{
return *pint;//返回迭代器指向元素的值
}
private:
int *pint;
}
使用如下:
vector<int> vec;
for(int i = 0;i < 10;i++)
vec.push_back(i);
my_input_iterator it(vec.begin());//自定义迭代器使用
//advance使用
std::cout<<"After advance 3:";
advance(it,3);
std::cout << *it << " "<<std::endl;//输出结果为3
好像写得差不多了,后面有空再写一点吧……end