07 迭代器和好用的新for迴圈

2022-10-09 18:57:07 字數 3333 閱讀 6434

迭代器是乙個很通用的概念,並不是乙個特定的型別。它實際上是一組對型別的要求([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...