C++STL迭代器实现原理之三:迭代器的实现与使用

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能够以某种顺序遍历首尾迭代器之间的所有元素
C++STL迭代器实现原理之三:迭代器的实现与使用_第1张图片
像上面的图展示的那样,我们平常熟悉的指针其实就是一种迭代器,通过对指针解引用就可以得到对应元素的值。这种情况下,指针就是地址,指针通过自增(减)运算可以向移动。如果一开始指针指向起点(begin),那么指针每自增一次,就向右移动一个元素,这样不断移动直到指向end位置,就可以遍历所有的元素。
虽然普通指针可以看成时迭代器,显然迭代器不仅仅是指针!迭代器像指针一样具有两个最基本的特性(功能):
(1)通过
运算可以得到迭代器指向元素的值
(2)通过++(或者–)可以移动迭代器指向元素的位置
当迭代器就是普通指针(数组的地址)时,运算就是指针的解引用,而++运算移动到下一个地址,这些都不需要我们自己去实现。
事实上,迭代器可以比指针更具有一般性。只要可以通过
实现解引用、通过++移动迭代器遍历元素等功能,都可以看做是指针,如下图所示。但是当迭代器不是普通的地址时,就需要我们自己通过重载运算符*、++等去实现相应功能。
C++STL迭代器实现原理之三:迭代器的实现与使用_第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_;
	publictypedef 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源码可以发现里面有下面几种迭代器:
C++STL迭代器实现原理之三:迭代器的实现与使用_第3张图片
不同的迭代器具有不同的读写功能。我们可以根据这些迭代器来设计我们自己的迭代器。设计的思路就是,让我们自己的迭代器继承上面的迭代器。

//设计一个输入型迭代器,只需要继承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

你可能感兴趣的:(c++,指针,数据结构)