迭代器适配器
http://blog.csdn.net/effective_coder/article/details/8733853
在不斷的演變中,STL的愛好者擴充了迭代器的內容,他們在迭代器的基礎上發展而來,叫迭代器適配器,他提供了更多的操作功能,也不僅僅局限于容器,還可以應用于更多方面。
首先看看迭代器適配器的分支圖:
?
以上的迭代器都叫做迭代器適配器,正如容器有標準容器和容器適配器,仿函數有標準仿函數和仿函數適配器,這里的迭代器適配器也是一樣的道理。顯然他們都是在原有迭代器的基礎之上發展而來的,所以基礎迭代器擁有的他們都擁有,包括上一節所講的幾個重要迭代器函數。下面我們將要一一的來講解上圖中的迭代器適配器。
1:逆向迭代器
逆向迭代器是一種適配器,他通過重新定義遞增遞減運算符使其行為恰好相反。這樣一來使用這種迭代器算法將會逆向次序進行。
定義方式:
Vector<int>::reserve_iterator?rit;
而且首位置為rbegin(),也就是原來的末位,末位置為rend(),也就是原來的首位;
請看下面一段代碼:
vector<int>?bec;
for(int?i=1;i<11;i++)
vec.push_back(i);
for_each(vec.rbegin(),vec.rend(),print);
//這里假定print為已經定義的輸出函數
這樣輸出結果可想而知,輸出的當然是10開始的逆序。
請注意看接下來的例子,他對你理解逆向迭代器至關重要。
?
結果:
?
這就奇怪了,同樣是pos所指的位置,該位置為5,為什么利用逆向迭代器輸出來以后就變成4了?現在我們需要討論一個問題,從輸出中我們知道結果已經變了,但是到底是迭代器所指的位置變了,還是迭代器的位置沒變,而是邏輯位置發生了改變。這里讀者可以自己猜一下。
接下來,廢話不多說,直接上圖!!!
?
有了這圖我就不需要再說什么了,相信大家已經對逆向迭代器理解了.下面我們將要介紹一個STL算法庫中針對逆向迭代器的函數。
Base()函數
該函數將逆向迭代器轉回正常的迭代器。在上面一個例子中已經用到了。結果也已經列出,不在多說。
對于逆向迭代器,就這些主要點的內容,改迭代器主要用來逆向輸出或者操作,只在一些特定的地方可以更好的發揮,但是也要掌握好他的用法,有時候處理起來還是比較方便。
2:輸入流輸出流迭代器
1:輸出流迭代器
輸出流迭代器也是一種適配器,他由輸出迭代器演變而來,賦予了寫入到輸出流中的能力,對于輸入輸出流這兩個迭代器,我認為最好的方式就是通過實例來說明問題,可以更好的學到用法。
輸出流迭代器定義:
Ostream_iterator<type>(ostream,delim);
或Ostream_iterator<type>(ostream,);
上面ostream代表需要為他的構造函數提供一個輸出流對象,我們一般是用cout,delim是一個字符串指針,表示以它的內容來隔開輸出的內容。
Iter++?和++iter?:表示迭代器往前移動,對下一個流單位進行寫入。
下面直接看這個例子。
?
?
可以看到利用輸出流迭代器輸出單個元素或者是輸出整個容器都是十分方便的,其中的copy函數是把begin和end之間的部分復制到inWriter中,實際就是復制到輸出流中,所以整個容器都輸出來了。這種方式在容器中是最常用的方法。
2:輸入流迭代器
對于輸入流迭代器,就是利用算法讀取輸入流中的數據,輸入輸出天生就是一對拍檔,但是輸入流卻要比輸出流復雜一點,因為讀取出來本來就要比寫入進去復雜。
定義:
利用構造函數構造一個輸入流迭代器的時候也需要傳遞一個輸入流對象作為參數,我們使用cin進行讀取,但是讀取動作可能發生的錯誤:讀取數據失敗或者是讀到了文件的尾部,因此我們利用了一個end_of_stream迭代器,他可以由istream_iterator的默認構造函數生成。
Istream_iterator:?????istream_iterator<type>?inRead(cin);
End_of_stream:??????istream_iterator<type>?inReadEOF;
只要輸入流迭代器有任何一次讀取失敗,他就會變成end_of_stream迭代器,所以每次讀取你都應該把他們兩個拿來比較一番。
還有個值得說明的是,輸入流迭代器的構造函數一旦構造他,就會馬上打開輸入流緩沖區,有時候會在構造之后馬上讀取一個元素,所以盡量不要過早的定義它,有可能導致第一個元素無法傳回。當然有些版本中會延緩這個動作,不會馬上讀取,這個作個了解就好。真要出現了我們也無法解決。
下面直接看例子:
?
可以看到,輸入的是那幾個數字只出現了前面幾個,那是因為我們構造函數傳遞的類型參數是int?所以她讀取到字符的時候導致讀取錯誤,迭代器變為end_of_stream迭代器,循環結束。
對于輸入流輸出流迭代器,希望大家多用,多熟悉,本節只是提及了一下最基本的,并不深入,但是你會發現處理很多問題還是很方便的,在以后有專門的章節介紹stream的那些知識。
3:安插型迭代器
Insert?迭代器用來將賦值和復制操作轉化為安插操作,(至于這句話是什么意思,我來分析一下,因為容器的操作都是賦值和復制操作,都會產生臨時對象,所以效率不高,利用安插型迭代器直接插入一個值,效率會更好,而且在某些方面,不僅僅是效率問題了。這就是安插型迭代器的理念。)
在介紹安插型迭代器之前,我們來看看copy算法的源代碼:
?
(只是模擬的,也許細節有出入,大概就這樣)
可以看到,copy從算法起點開始不斷循環,不斷賦值給to_pos,直到循環結束。于是有初學者直到了copy算法之后就開始這樣使用,代碼如下:
?
?
結果大家可以猜想一下,這里我直接貼出來:
有經驗的一看就是內存錯誤,也許是越界咯!
那么到底是什么原因勒?我們知道copy算法是依次賦值,我相信只要學過編程的都知道,要想給一個變量賦值,首先那個變量得有地址吧!要分配空間吧,不能給一個不存在的變量賦值吧,那么在這里,a容器使用默認構造函數,構造的是一個空容器,不存在任何長度,也就沒分配那么多空間,如何存儲得下b的元素,失敗是必然的。
這里順便提一句,?在標準的C++算法庫中,沒有任何一個算法在不借助外來幫助的情況下可以改變容器的大小,注意我說的是算法庫中,不是指push_back這一類容器自帶的函數。
所以在這里,我們的安插型迭代器出現用途了,這就是上面所說的借助的外來幫助,安插性容器本來自帶了分配內存并且插入的功能
改寫后的程序,代碼如下:
在代碼中我只是修改了其中一個地方,?利用了后插迭代器的便捷函數,也許大家還對安插迭代器陌生,前面這一切只是個鋪墊,下面我們真正的開始介紹安插迭代器。
安插性迭代器分類:
1:前插迭代器。(默認為只在最前面插入)
2:后插迭代器。(默認為只能在最后插入)
3:插入迭代器(也就是可以指出插入位置的迭代器)
實際上他們都是調用相應容器的插入函數。
每一種迭代器都可以由一個便捷函數加以生成和初始化(便捷函數不陌生吧,pair模版和make_pair函數)
請記住下面這個表:
?
| 名稱 | 迭代器類名?class | 調用相應的函數 | 便捷函數 |
| 后插迭代器 | back_insert_iterator | Push_back(value) | Back_inserter |
| 前插迭代器 | front_insert_iterator | Push_front(value) | Front_inserter |
| 插入迭代器 | Insert_iterator | Insert(pos,value) | Inserter |
?
對于迭代器的舉例,這里我只列出后插的即可,代碼如下:
?
?
?
從這個程序中我們可以看到,后插迭代器的初始化,首先需要指明容器類型,然后還要賦一個具體的容器,使他們綁定在一起,初始化方法需要掌握。接下來利用的是便捷函數back_inserter的用法,可以看出他比較方便,最后一段代碼表示利用copy算法把本身前面的一段插入到后面一段,但是也許有人不明白,為什么最后一個數值出錯了勒?大家可以考慮一下這個原因是什么,上面一個例子我已經說了,安插迭代器可以在不分配內存的情況下直接插入,所以這里只是錯誤,但不至于程序崩潰。在對容器自身進行插入時最好是分配好足夠的空間,因為這樣可以使原有的迭代器固定下來,執行插入也不容易出錯。如果不固定容器大小則他的迭代器會經常改變,這也就是導致錯誤的原因。
解決辦法,在上面的代碼中加入這一行,我相信大家都知道加在哪:
coll.reserve(2*coll.size());?//把容器長度重置為原來的兩倍。
?
?
?
安插型迭代器本身的內容就不多,把前面的理解了就可以了,至于更高級的用法,這需要與算法庫中的函數結合才能發揮出效果。
迭代器適配器就到這里,我們主要分析了三種適配器的用法和注意點,合理的運用適配器會使程序更加方便簡潔,本節我略去了幾個知識,一個是為迭代器編寫泛型函數,一個是使用自定義的迭代器,這些過于高級,我自己也一知半解,所以不能誤人子弟,以后有機會我會補充出來的。
總結
- 上一篇: Android显示横幅样式通知
- 下一篇: 尼尔机器人技能快捷键_《尼尔:机械部队》