迭代器是乙個很通用的概念,並不是乙個特定的型別。它實際上是一組對型別的要求([1])。它的最基本要求就是從乙個端點出發,下一步、下一步地到達另乙個端點。按照一般的中文習慣,也許「遍歷」是比「迭代」更好的用詞。我們可以遍歷乙個字串的字元,遍歷乙個檔案的內容,遍歷目錄裡的所有檔案,等等。這些都可以用迭代器來表達。
以上這些迭代器只考慮了讀取。如果乙個型別像輸入迭代器,但 *i 只能作為左值來寫而不能讀,那它就是個輸出迭代器
基本要求是:
迭代器通常是物件。但需要注意的是,指標可以滿足上面所有的迭代器要求,因而也是迭代器。
最常用的迭代器就是容器的 iterator 型別了。以我們學過的順序容器為例,它們都定義了巢狀的 iterator 型別和 const_iterator 型別。一般而言,iterator 可寫入,const_iterator 型別不可寫入,但這些迭代器都被定義為輸入迭代器或其派生型別:
#include // std::copy
#include // std::back_inserter
#include // std::vector
using namespace std;
vectorv1;
vectorv2;
copy(v1.begin(), v1.end(),
back_inserter(v2));
另外乙個常見的輸出迭代器是 ostream_iterator,方便我們把容器內容「拷貝」到乙個輸出流。
#include // std::cout
copy(v2.begin(), v2.end(),
ostream_iterator(cout, " "));
下面我們來看一下乙個輸入迭代器。它的功能本身很簡單,就是把乙個輸入流(istream)的內容一行行讀進來。配上 c++11 引入的基於範圍的 for 迴圈的語法,我們可以把遍歷輸入流的**以一種自然、非過程式的方式寫出來,如下所示:
for (const string& line :
istream_line_reader(is))
對比傳統c++
```c++
string line;
for (;;)
cout << line << endl;
}## 基於範圍的 for 迴圈這個語法
```c++
}
可以看到,它做的事情也不複雜,就是:
生成迭代器這一步有可能是——但不一定是——呼叫 r 的 begin 和 end 成員函式。具體規則是:
c++ 裡有些固定的型別要求規範。對於乙個迭代器,我們需要定義下面的型別
class istream_line_reader ;
…};
仿照一般的容器,我們把迭代器定義為 istream_line_reader 的巢狀類。
它裡面的這五個型別是必須定義的(其他泛型 c++ **可能會用到這五個型別;之前標準庫定義了乙個可以繼承的類模板 std::iterator 來產生這些型別定義,但這個類目前已經被廢棄)。
其中:關於iterator_category請參考 《stl原始碼剖析》
作為乙個真的只能讀一次的輸入迭代器,有個特殊的麻煩(前向迭代器或其衍生型別沒有):到底應該讓 * 負責讀取還是 ++ 負責讀取。我們這兒採用常見、也較為簡單的做法,讓 ++ 負責讀取,* 負責返回讀取的內容(這個做法會有些***,但按我們目前的用法則沒有問題)。這樣的話,這個 iterator 類需要有乙個資料成員指向輸入流,乙個資料成員來存放讀取的結果。根據這個思路,我們定義這個類的基本成員函式和資料成員:
class istream_line_reader
explicit iterator(istream& is)
: stream_(&is)
reference operator*() const noexcept //返回當前的字串
pointer operator->() const noexcept
iterator& operator++()
return *this;
}iterator operator++(int)
private:
istream* stream_; //真正指向的資源
string line_; //待返回的字串
};…};
我們定義了預設建構函式,將 stream_ 清空;相應的,在帶引數的建構函式裡,我們根據傳入的輸入流來設定 stream_。我們也定義了 * 和 -> 運算子來取得迭代器指向的文字行的引用和指標,並用 ++ 來讀取輸入流的內容(後置 ++ 則以慣常方式使用前置 ++ 和拷貝構造來實現)。唯一「特別」點的地方,是我們在建構函式裡呼叫了 ++,確保在構造後呼叫 * 運算子時可以讀取內容,符合日常先使用 *、再使用 ++ 的習慣。一旦檔案讀取到尾部(或出錯),則 stream_ 被清空,回到預設構造的情況。
對於迭代器之間的比較,我們則主要考慮檔案有沒有讀到尾部的情況,簡單定義為:
bool operator==(const iterator& rhs)
const noexcept
bool operator!=(const iterator& rhs)
const noexcept
有了這個 iterator 的定義後,istream_line_reader 的定義就簡單得很了:
class istream_line_reader ;
istream_line_reader() noexcept
: stream_(nullptr) {}
explicit istream_line_reader(
istream& is) noexcept
: stream_(&is) {}
iterator begin()
iterator end() const noexcept
private:
istream* stream_;
};
也就是說,建構函式只是簡單地把輸入流的指標賦給 stream_ 成員變數。begin 成員函式則負責構造乙個真正有意義的迭代器;end 成員函式則只是返回乙個預設構造的迭代器而已。
完整的工程可用**,請參見參考資料。該專案中還提供了利用 c 檔案介面的 file_line_reader 和基於記憶體對映檔案的 mmap_line_reader。
python中的for迴圈(迭代器機制)
python中的for迴圈 1 x hello 2 for i in x iter x x.iter 3 print i iter x.next iter l x.iter 遵循迭代器協議,生成可迭代物件 print iter l.next for迴圈和索引沒關係 基於迭代器機制 對列表的索引 超出...
python中的for迴圈(迭代器機制)
python中的for迴圈 1 x hello 2 for i in x iter x x.iter 3print i iter x.next iter l x.iter 遵循迭代器協議,生成可迭代物件 print iter l.next for迴圈和索引沒關係 基於迭代器機制 對列表的索引 超出索...
內建迭代器的雙向迴圈鍊錶
有些日子沒有寫文章了.最近重新看了一下資料結構和演算法設計.寫了個雙向迴圈鍊錶,這玩意兒倒不難.既然要適應多種型別的需求,當然要用類模板了.ifndef list h define list h template typename t class list node const t e,node p...