iterator usage

来源:互联网 发布:ubuntu 离线安装jre 编辑:程序博客网 时间:2024/06/06 01:25

前言:

  以下的内容为我阅读c++沉思录18,19,20章的笔记以及自己的想法.

正文:

  总所周知,c++的stl中提出了iterator的概念,这是C所没有的.在一般的使用中,iterator的行为很像c内建的指针.而在java和c#中索性就直接取消了指针,而采用类似iterator的做法来代替了指针.很多编程人员在使用iterator的时候也仅仅把他当作了指针的一个变体而没有多加注意.

  不过既然是学习,那我们在使用的时候也要知道其存在的原因,其分类以及用法吧.

  首先是问题的提出:

  很多人会觉得,既然C++沿用了C的指针这么强大的东西了,为什么还要iterator这么一群类来工作呢?

  我们知道,在我们编写模板的时候,对于使用了iterator做为参数的函数,往往该函数对于iterator有些特定的操作.比如下列2个函数

      

复制代码
template<class P,class T>P find(P start,P beyond,const T& x){       while( start != beyond && * start != x)              ++start;       return start;}template<class P, class T>void reverse(P start, P beyond){   while(start != beyond) {        --beyond;        if (start != beyond) {           T t = *start;           *start = *beyond;           *beyond = t;           ++ start;        }    }}
复制代码

  我们可以看到,这两个函数都对模板参数P做了一定要求,在find中,我们要求P必须允许 != ,++和*(P)这三个运算符的操作,而对于reverse函数来说,其要求更多,运算符++,--,*(),!=都必须支持.问题就这么出来了,我们怎么让所有人都遵守这一要求呢?或者说,后续采用这个模板的使用者怎么能在不知道实现细节的情况下了解并遵守这些要求呢?显然,我们需要一个分类方法来知道如何界定不同种类的迭代器.

  不过这里还是没有解释一个疑惑,即这两个函数改成T的指针也能完成的很好,我们还要iterator做什么?

  答案很简单,不是所有数据结构都是线性的!对于一个链表来说,如果要实现上述的功能,我们必须重新写一个几乎一样仅仅是把++start变成start = start->next的函数.显然,这样重复的事情我们是不希望碰到的,尤其如果函数还非常大的情况下.而这时候,iterator的作用就出来了.对于链表,我们的链表的iterator(list_iterator)仅仅只要把operator++()重写成operator++(){now = now->next;},就能正常的使用find函数了.完全不需要重新编写.这就减少了我们算法的编写量.

  现在,既然我们知道了iterator的好处了之后,接下来我们就想知道之前提到的分类法是怎么回事了.经常听说输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机存取迭代器是怎么回事.

  迭代器的分类:

  1.输入迭代器(input iterator)

        input iterator就像其名字所说的,工作的就像输入流一样.我们必须能

  • 取出其所指向的值  
  • 访问下一个元素
  • 判断是否到达了最后一个元素
  • 可以复制

  因此其支持的操作符有  *p,++p,p++,p!=q,p == q这五个.凡是支持这五个操作的类都可以称作是输入迭代器.当然指针是符合的.

  2.输出迭代器(output iterator)

    output iterator工作方式类似输出流,我们能对其指向的序列进行写操作,其与input iterator不相同的就是*p所返回的值允许修改,而不一定要读取,而input只允许读取,不允许修改.

    支持的操作和上头一样,支持的操作符也是 *p,++p,p++,p!=q,p == q.

 

  使用Input iterator和output iterator的例子:

 

复制代码
 1 template<class In,class Out> 2 void copy(In start,In beyond, Out result) 3 { 4      while(start != beyond) { 5          *result = *start; //result是输出迭代器,其*result返回的值允许修改 6          ++result; 7          ++start; 8       } 9 }10 11 //简写12 template<class In,class Out>13 void copy(In start,In beyond, Out result)14 {15      while(start != beyond)16         *result++ = *start++;//这个应该能看懂的...17 }
复制代码


   3.前向迭代器(forward iterator)

     前向迭代器就像是输入和输出迭代器的结合体,其*p既可以访问元素,也可以修改元素.因此支持的操作也是相同的.

   4.双向迭代器(bidirectional iterator)

     双向迭代器在前向迭代器上更近一步,其要求该种迭代器支持operator--,因此其支持的操作有  *p,++p,p++,p!=q,p == q,--p,p--

   5. 随机存取迭代器(random access iterator)

    即如其名字所显示的一样,其在双向迭代器的功能上,允许随机访问序列的任意值.显然,指针就是这样的一个迭代器.

    对于随机存取迭代器来说, 其要求高了很多:

  • 可以判断是否到结尾(  a==b or a != b)
  • 可以双向递增或递减( --a or ++a)
  • 可以比较大小( a < b or a > b or a>=b ...etc)
  • 支持算术运算( a + n)
  • 支持随机访问( a[n] )
  • 支持复合运算( a+= n)

  结构图:

  上面即为我们所讨论到的几个迭代器的层次.看起来很像继承是麽?

  但是实际上在STL中,这些并不是通过继承关联的.这些只不过是一些符合条件的集合.这样的好处是:少去了一个特殊的基类(input iterator),其次,使得内建类型指针可以成为iterator.

      其实只要明白iterator是满足某些特别的功能的集合(包括类和内建类型),就不会觉得是继承了.

   




example:

不多说,先看代码:

[cpp] view plaincopyprint?
  1. #include <IOSTREAM>  
  2. #include <VECTOR>  
  3.   
  4. using namespace std;  
  5.   
  6. int main()  
  7. {  
  8.     cout<<"By:Sayln_wu"<<endl<<endl;  
  9.     //编写10个元素的vector对象。用迭代器把每个元素值改为当前值+2.  
  10.     vector<int> ivec(3,8);  
  11.     cout<<"原值为:"<<endl;  
  12.     int i=0;  
  13.     for (vector<int>::iterator iter=ivec.begin();iter!=ivec.end();iter++)  
  14.     {  
  15.         i++;  
  16.         cout<< *iter <<endl;  
  17.         *iter=(*iter)+i;  
  18.     }  
  19.     //可以放在上面输出,下面是为了使用const_iterator。  
  20.     cout<<"修改后的值为:"<<endl;  
  21.     for(vector<int>::const_iterator citer=ivec.begin();citer!=ivec.end();citer++)  
  22.     {  
  23.         cout<<*citer<<endl;  
  24.     }  
  25.     //求vector中间值  
  26.     vector<int>::iterator min = ivec.begin() +ivec.size()/2;  
  27.     cout<<"中间值为:"<<*min<<endl;  
  28.     return 0;  
  29. }  

截图如下:

在上述代码中用到了两个iterator,一个是iterator,另一个是const_iterator。const_iterator是一个只读的迭代器。

当然还有一个是const iterator ,这个也可以,不过不能指定指定位置。所以很少使用。

*iter 是读取ivec的当前值。    *操作符为解引用操作符。

0 0
原创粉丝点击