該頭文件圍繞迭代器展開,定義了一系列與迭代器有關的概念,但最最最重要的一點就是----它和其它容器一起實現了C++容器的Iterator設計模式。
Iterators are a generalization of pointers that allow a C++ program to work with different data structures(containers) in a uniform manner.
上述文字摘自C++14標准草案,簡而言之,迭代器就是對指針的一層封裝,提供了統一的接口。
使用迭代器有很多好處:
詳細請見設計模式。
迭代器主要有5類([iterator-class]代指該類迭代器支持的操作集):
這裡有兩點需要特別說明:
不難看出,這幾類迭代器有如下關系:
因為迭代器實際上是指針的抽象,很多功能概念都是從指針身上“扒”下來的,所以它的語義跟指針是一致的。
這意味著什麼呢? 這意味著可以傳入指針作為迭代器, 因為指針上的操作集(遞增、遞減、算數運算等)是迭代器的超集,模版定義對迭代器所做出的操作要求放在指針上完全適用。
這在操縱內置數組的時候,可以省去不少麻煩(不用再去敲多余的代碼來生成iterators):
int numbers[] = { 1,2,3,4 };
std::find(numbers, numbers + 4, 2);
標准庫提供了以下4個方面的設施來幫助用戶使用iterator。
“traits”是特性的意思,所以“iterator_traits”是迭代器特性的意思。 從代碼角度看,這裡的traits就是types,因為這個類只包含了五個類型定義:
pointer
iterator_category
因為algorithm在C++是單獨的一塊,是iterator將容器與算法溝通在一起。 也就是說,標准庫的算法只是通過迭代器來進行數據操作。 必然而然的,一些操作需要有對應的類型: 1) 例如,應用distance庫函數計算迭代器的距離,應該返回“距離”類型的值。 2) 例如,獲取迭代器指向的對象,應該返回“對象值”類型的對象。 等等...
所以,標准庫的算法需要我們定義這些類型,好讓它在應用算法的時候使用正確的類型。
需要注意的是,當迭代器為output iterators時,上面的4個類型可能被定義為void(可能對於output iterator來說,這四個類型都沒有多大意義,它支持的操作非常有限)。
上面這個iterator_traits類取自某個庫的iterator實現,可以看到,默認的iterator_traits模版內的類型定義都取自迭代器中相應的類型,即_Iter迭代器類。 所以我們在定義自己的迭代器的時候,如果定義了這些類,就不用再顯示實例化iterator_traits模版了,它能自動提取出這些類型。
這時候就輪到我們的iterator類來大顯身手啦! 用戶只要繼承這個base class並指定兩個參數,就可以獲得剩余的三個類型定義,因為它的定義是這樣的:
對應迭代器類別,這裡也有5類標簽(tag),名稱為XXX_tag,XXX對應迭代器名稱。
這個標簽的作用主要是實現標簽派發功能,提供迭代器類型信息,從而讓C++庫算法可以選擇合適的、高效的操作來完成算法(可參見下一小節)。
頭文件還提供了一些方便的操縱迭代器的函數供用戶使用:
對於不同類型的迭代器,上述四個函數采用不同的方法進行計算,例如:
標准庫包含了三種迭代器適配器:
Insert iterator:通過迭代器進行元素的插入時,操作略有不同。 指針通常是指向已有的內存,因此迭代器一般也只是對指向地址的元素進行賦值;而插入元素是需要先分配內存,再賦值。 為了能讓使用者像使用一般迭代器那樣進行元素的插入,標准庫提供了3種插入迭代器:
Move iterator:移動迭代器會將內部的迭代器的操作返回值全部轉換成右值(rvalue)。
這些都是全局模版函數,利用類型推導幫助用戶構造上述的三種迭代器適配器。
輸入輸出一直是語言非常重要的部分,對於C++迭代器來說,操縱流(stream)中數據的輸入輸出的重要性毋庸置疑。 Stream Iterator則是針對stream的一套迭代器,包括istream,ostream,istreambuf 和 ostreambuf。
以下兩種是input iterator:
以下兩種是output iterator: