文章目錄
- 前言
- 插入迭代器
- inserter
- front_inserter
- back_inserter
- iostream迭代器
- istream_iterator 讀取輸入流
- istream_iterator允許使用懶惰求值
- ostream_iterator操作
- 反向迭代器
- reverse_iterator的base成員函數
前言
除了為每個容器定義的迭代器之外,標準庫在頭文件iterator中還定義了額外幾種迭代器。這些迭代器包括以下幾種。
- 插入迭代器(insert iterator):這些迭代器被綁定到一個容器上,可用來向容器插入元素。
- 流迭代器(stream iterator):這些迭代器被綁定到輸入或輸出流上,可用來遍歷所關聯的IO流。
- 反向迭代器(reverse iterator):這些迭代器向后而不是向前移動。除了forward_list之外的標準庫容器都有反向迭代器。
- 移動迭代器(move iterator):這些專用的迭代器不是拷貝其中的元素,而是移動它們。
插入迭代器
插入器有三種類型,差異在于元素插入的位置:
- back_inserter創建一個使用push_back的迭代器。
- front_inserter創建一個使用push_front的迭代器。
- inserter創建一個使用insert的迭代器。此函數接受第二個參數,這個參數必須是一個指向給定容器的迭代器。元素將被插入到給定迭代器所表示的元素之前。
inserter
當調用inserter(c,iter)
時,我們得到一個迭代器,接下來使用它時,會將元素插入到iter原來所指向的元素之前的位置。即,如果it是由inserter生成的迭代器,則下面這樣的賦值語句:
*it = val;
等價于:
it = c.insert(it, val); // it指向新加入的元素val
++it; // 遞增it使其指向原來的元素
front_inserter
一直向首元素位置添加元素,添加后指向新的首元素。
以以下實例區分inserter和front_inserter的區別:
back_inserter
向容器尾部不斷添加元素。
以unique_copy函數為實例來觀察back_inserter的作用:
值得注意的是,與unique一樣,unique_copy也要求在源容器中重復元素是相鄰存放的,因此容器如果無序且重復元素未相鄰存放,unique_copy會失敗,穩妥起見應該先對vector排序。
vector<int> vi = {2, 6, 7, 13, 15, 20, 22, 23, 25, 30};list<int> li;ostream& os(cout);int n = 10;while(n--){vi.push_back(n);}sort(vi.begin(), vi.end());for(auto i : vi){os << i << ends;}os << endl;unique_copy(vi.begin(), vi.end(), back_inserter(li));for(auto i : li){os << i << ends;}os << endl;
輸出結果:
iostream迭代器
雖然iostream類型不是容器,但是標準庫定義了可以用于這些IO類型對象的迭代器。
istream_iterator 讀取輸入流
流迭代器不支持遞減運算,因為不可能在一個流中反向移動。
istream_iterator使用>>來讀取流。因此,istream_iterator要讀取的類型必須定義了輸入運算符。
- 當創建一個istream_iterator時,可以將它綁定到一個流
- 默認初始化相當于創建一個可以當作尾后值使用的迭代器。
用istream_iterator從標準輸入讀取數據,存入一個vector:
istream_iterator可以用來構建容器:
istream_iterator<int> in_iter(cin);
istream_iterator<int> eof;
vector<int> vec(in_iter, eof);
本例中我們用一對表示元素范圍的迭代器來構造vec。這兩個迭代器是istream_iterator,這意味著元素范圍是通過從關聯的流中讀取數據獲得的。這個構造函數從cin中讀取數據,直至遇到文件尾或者遇到一個不是int的數據為止。從流中讀取的數據被用來構造vec。
istream_iterator允許使用懶惰求值
當我們將一個istream_iterator綁定到一個流時,標準庫并不保證迭代器立即從流讀取數據。 具體實現時可以推遲從流中讀取數據的時機——直到我們使用迭代器時才真正讀取。標準庫中的實現所保證的是,在我們第一次解引用迭代器之前,從流中讀取數據的操作已經完成了。 對于大多數程序來說,立即讀取還是推遲讀取沒什么差別。但是,如果我們創建了一個istream_iterator,沒有使用就銷毀了,或者我們正在從兩個不同的對象同步讀取同一個流,那么何時讀取可能就很重要了。
ostream_iterator操作
可以對任何具有輸出運算符(<<運算符)的類型定義ostream_iterator。
當創建一個ostream_iterator時,我們可以提供(可選的)第二參數,它是一個字符串,在輸出每個元素后都會打印此字符串。此字符串必須是一個C風格字符串(即,一個字符串字面常量或者一個指向以空字符結尾的字符數組的指針)。
與iostream_iterator不同的是:必須將ostream_iterator綁定到一個指定的流,不允許空的或表示尾后位置的ostream_iterator。
可以用ostream_iterator來輸出值的序列:
此程序將vec中的每個元素寫到cout,每個元素后加一個空格。每次向out_iter賦值時,寫操作就會被提交。
值得注意的是,當我們向out_iter賦值時,可以忽略解引用和遞增運算。即,循環可以重寫成下面的樣子:
istream_iterator<int> in_iter(cin);
istream_iterator<int> eof;
vector<int> vec(in_iter, eof);
ostream_iterator<int> out_iter(cout, " ");
for(auto i : vec){out_iter = i;
}
cout << endl;
運算符*和++實際上對ostream_iterator對象不做任何事情,因此忽略它們對我們的程序沒有任何影響。 但是,推薦第一種形式。在這種寫法中,流迭代器的使用與其他迭代器的使用保持一致。如果想將此循環改為操作其他迭代器類型,修改起來非常容易。而且,對于讀者來說,此循環的行為也更為清晰。
可以通過調用copy來打印vec中的元素,這比編寫循環更為簡單:
copy(vec.begin(), vec.end(), out_iter);
cout << endl;
可以用unique_copy代替copy只打印不重復元素:
反向迭代器
反向迭代器就是在容器中從尾元素向首元素反向移動的迭代器。對于反向迭代器,遞增(以及遞減)操作的含義會顛倒過來。遞增一個反向迭代器(++it)會移動到前一個元素;遞減一個迭代器(- -it)會移動到下一個元素。
反向迭代器逆序打印vector<int>
中的元素:
對照普通迭代器可以看出,rbegin指向尾元素,rend指向首元素前,遞增和遞減操作的含義會顛倒過來:反向迭代器的遞增操作會移動到前一個元素。
反向迭代器也有十分方便的時候,比如不加額外參數就可以使得元素降序排列:
reverse_iterator的base成員函數
base成員函數用來將反向迭代器轉換成其對應的普通迭代器(當然反過來也是可以的,普通迭代器轉換成對應的反向迭代器):
圖中的對象顯示了普通迭代器與反向迭代器之間的關系。例如,rcomma和rcomma.base()指向不同的元素,line.crbegin和line.cend()也是如此。這些不同保證了元素范圍無論是正向處理還是反向處理都是相同的。
從技術上講,普通迭代器與反向迭代器的關系反映了左閉合區間的特性。關鍵點在于[line.crbegin(),rcomma)
和[rcomma.base(),line.cend())
指向line中相同的元素范圍。為了實現這一點,rcomma和rcomma.base()必須生成相鄰位置而不是相同位置,crbegin()和cend()也是如此。