前言:html
如下的内容为我阅读c++沉思录18,19,20章的笔记以及本身的想法.java
正文:ios
总所周知,c++的stl中提出了iterator的概念,这是C所没有的.在通常的使用中,iterator的行为很像c内建的指针.而在java和c#中索性就直接取消了指针,而采用相似iterator的作法来代替了指针.不少编程人员在使用iterator的时候也仅仅把他看成了指针的一个变体而没有多加注意.c++
不过既然是学习,那咱们在使用的时候也要知道其存在的缘由,其分类以及用法吧.算法
首先是问题的提出:编程
不少人会以为,既然C++沿用了C的指针这么强大的东西了,为何还要iterator这么一群类来工做呢?c#
咱们知道,在咱们编写模板的时候,对于使用了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作什么?dom
答案很简单,不是全部数据结构都是线性的!对于一个链表来讲,若是要实现上述的功能,咱们必须从新写一个几乎同样仅仅是把++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)
即如其名字所显示的同样,其在双向迭代器的功能上,容许随机访问序列的任意值.显然,指针就是这样的一个迭代器.
对于随机存取迭代器来讲, 其要求高了不少:
结构图:
上面即为咱们所讨论到的几个迭代器的层次.看起来很像继承是麽?
可是实际上在STL中,这些并非经过继承关联的.这些只不过是一些符合条件的集合.这样的好处是:少去了一个特殊的基类(input iterator),其次,使得内建类型指针能够成为iterator.
其实只要明白iterator是知足某些特别的功能的集合(包括类和内建类型),就不会以为是继承了.
Iterator使用:
一个ostream_iteartor的例子:
1 #include <iostream>
2
3 using namespace std;
4
5 template<class T>
6 class Ostream_iterator {
7 public:
8 Ostream_iterator(ostream &os,const char* s):
9 strm(&os), str(s){}
10 Ostream_iterator& operator++() {return *this;}
11 Ostream_iterator& operator++(int) {return *this;}
12 Ostream_iterator& operator*() {return *this;}
13 Ostream_iterator& operator=(const T& t)
14 {
15 *strm << t << str;
16 return *this;
17 }
18
19 private:
20 ostream* strm;
21 const char *str;
22 };
23
24 template<class In,class Out>
25 Out Copy(In start,In beyond,Out dest)
26 {
27 while(start != beyond)
28 *dest++ = *start++;
29 return dest;
30 }
31
32 int main()
33 {
34 Ostream_iterator<int> oi(cout, " \n");
35
36 int a[10];
37 for (int i = 0;i!=10;++i)
38 a[i] = i+1;
39 Copy(a,a+10,oi);
40
41 return 0;
42 }
在这个例子中,咱们简单的构造了一个ostream_iterator,而且使用了copy来输出..这个ostream_iterator和STL差异仍是很大,不过功能上已经差很少了.我想读者们应该也能看懂代码吧,因此就不用多作什么解释了.
第二个例子中,咱们讲构造一个istream_iterator.
对于一个istream_iterator来讲,咱们必需要存的是T buffer和istream *strm.不过因为istream的特殊性,咱们必须知道buffer是否满,以及是否到达尾部,所以,咱们的结构是这样的
1 template <class T>
2 class Istream_Iterator{
3
4 private:
5 istream *strm;
6 T buffer;
7 int full;
8 int eof;
9 };
对于构造函数来讲,由于有eof的存在,所以,咱们天然想到,咱们的默认构造函数就应该把eof设为1.而对于有istream的iterator,都应该为0.
1 Istream_iterator(istream &is):
2 strm(&is),full(0),eof(0){}
3 Istream_iterator():strm(0),full(0),eof(1){}
对于咱们的版本的istream_iterator来讲,咱们须要完成的功能有
自增操做:
咱们知道,在istream中,一旦获取到一个值了以后,咱们将不在访问这个值.咱们的iterator也要符合这样的要求,所以,咱们经过full来控制.
1 Istream_iterator& operator++(){
2 full = 0;
3 return *this;
4 }
5 Istream_iterator operator++(int){
6 Istream_iterator r = *this;
7 full = 0;
8 return r;
9 }
Dereference:
对于解除引用操做来讲,咱们须要将流中缓存的值存入buffer内.同时,咱们要判断读取是否到结尾,到告终尾则应当出错.
在这里咱们写了一个私有函数fill(),这个将在比较的时候用到
T operator*() {
fill();
assert(eof);//咱们判定不该该出现eof
return buffer;
}
void fill(){
if (!full && !eof) {
if (*strm >> buffer)
full = 1;
else
eof = 1;
}
}
比较:
对于比较来讲,只有两个istream_iterator都同一个对象或者都处于文件尾时,这两个istream_iterator才相等.所以这里其比较对象为istream_iterator而不是const istream_iterator
template<class T>
int operator==(Istream_iterator<T> &p,Istream_iterator<T>& q)
{
if (p.eof && q.eof)
return 1;
if (!p.eof && !q.eof)
return &p == &q;
p.fill();q.fill();
return p.eof == q.eof;
}
最后的测试例子,读者能够把以前的copy和ostream_iterator拷下来
int main()
{
Ostream_iterator<int> o(cout,"\t");
Istream_iterator<int> i(cin);
Istream_iterator<int> eof;
Copy(i,eof,o);
return 0;
}
结论:
iterator是STL实现全部算法已经其通用型的基础.经过对iterator分类,使得算法的使用者在使用时不须要知道具体实现就可知道算法对于参数的要求,造成一个通用的体系.