前言: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分類,使得算法的使用者在使用時不須要知道具體實現就可知道算法對於參數的要求,造成一個通用的體系.
原文轉自:http://www.cnblogs.com/marchtea/archive/2012/02/27/2370068.html
原做者爲 marchtea. 請尊重原做者版權