###### tags: `cpp` # CPP Iterator簡介 iterator是來解決pointer走訪時,容易亂指所導致的問題 像是pointer走訪array,`*(ptr+0)` 到 `*(ptr+n)`多走一個都會導致走訪錯誤,更不用說比array複雜的資料結構走訪了 所以就延伸出iterator,有類似pointer的走訪功能,但能像在走訪前做一些判斷或功能,這樣就能安全、方便的走訪 而他的防呆或判斷方式,就是用[運算子重載](https://www.geeksforgeeks.org/operator-overloading-cpp/)的方式,去定義每個資料結構如何走訪下一步 ## 大略原理 先看一段基本使用 ```cpp int main() { vector<int> ar = {1, 2, 3, 4, 5}; // vector的iterator vector<int>::iterator ptr; cout << "All elements : "; for (ptr = ar.begin(); ptr != ar.end(); ptr++) cout << *ptr << " "; // output: 1 2 3 4 5 return 0; } ``` 以最常用的走訪來說,首先要知道"從哪開始(begin)"、"怎樣算結束(end)"、"如何找到下一個(next)" 所以vector內部可能就會定義這樣一些iterator需要的東西,然後就會有個走訪的模板去操作iterator,像是for_each、copy、reverse等等 ```cpp struct Iterator { pointer m_ptr; // Prefix increment Iterator& operator++() { m_ptr++; return *this; } friend bool operator== (const Iterator& a, const Iterator& b) { return a.m_ptr == b.m_ptr; }; friend bool operator!= (const Iterator& a, const Iterator& b) { return a.m_ptr != b.m_ptr; }; Iterator begin() { return Iterator(&m_data[0]); } Iterator end() { return Iterator(&m_data[200]); } // 200 is out of bounds }; ``` iterator裡面operator++()這個運算重載,通常不會是單純的`m_ptr++`,只是舉個栗子,通常前面會加一些判斷是否超出範圍,或是一些計算來取得下一個東西的位置 ## Iterator的走訪 前面都定義好行為後,應該就更了解這段的運作 ```cpp vector<int> ar = {1, 2, 3, 4, 5}; cout << "All elements : "; for (ptr = ar.begin(); ptr != ar.end(); ptr++) cout << *ptr << " "; ``` ## 透過For_each 那上面這個其實還是滿麻煩的對ㄅ,所以就把這串整合成一個通用函示,叫做 [for_each](https://en.cppreference.com/w/cpp/algorithm/for_each) ```cpp UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f) { for (; first != last; ++first) { f(*first); } return f; // implicit move since C++11 } ``` 透過for_each操作 ```cpp void print(int n) { cout << n << " "; } int main() { vector<int> v1 = {1, 2, 3, 4, 5}; cout << "use for_each : "; for_each(v1.begin(), v1.end(), print); // output: 1 2 3 4 5 return 0; } ``` 把Iterator的for_each簡化,演變成用`for(:)` ```cpp= int main() { vector<int> v1 = {1, 2, 3, 4, 5}; cout << "use for_each : "; for (auto n : v1) cout << n << " "; // output: 1 2 3 4 5 return 0; } ``` 然你就會發現,越來越簡化,而且邏輯很清晰好懂 所以iterator就是把pointer的走訪功能,把它包裝起來,變得方便易用,又不容易出問題 iterator裡面的operator++()這個運算重載,通常不會是單純的 m_ptr++,只是舉個栗子,通常前面會加一些判斷是否超出範圍,或是一些計算來取得下一個東西的位置 像是map的++,就會去走訪樹,然後找出下一個 這樣,就比單純用指標走訪強很多了,為了因應複雜結構的走訪,所以才搞出這東西 ## 應用 最直接的範例就是紅黑樹的資料結構,因為樹的走訪不是循序的,沒辦法向陣列指標一樣,透過`treeNode++`的方式去取得下一個資料節點,但借助Iterator的特性,就能方便的走訪節點 而這個東西就叫做std::map,map的`++`或是`next()`,就會去走訪樹,然後找出下一個 這裡以3->1->5->4->2的順序插入資料,根據map紅黑樹會排序的特性,可以得到遞增的順序輸出 ```cpp map<int, string> myMap; myMap[3] = "ttt"; myMap[1] = "www"; myMap[5] = "fff"; myMap[4] = "ggg"; myMap[2] = "rrr"; for (auto it : myMap) { cout << it.first << " " << it.second << "\n"; } output: 1 www 2 rrr 3 ttt 4 ggg 5 fff ``` 以tree這種資料結構,像要按順序輸出,還要寫一個中序走訪 在map這種由節點構成的結構,能把走訪的方式包裝起來,輕鬆使用,這就是Iterator最大的用處 ## 總結 雖然正常走訪pointer也做得到,但Iterator的優勢就在於 - 能透過運算子重載的方式,去**指定下一個目標**在哪,讓資料結構不侷限於陣列這種連續空間上 - 將走訪的方式包裝起來,更方便的操作 - 避免指標超出存取範圍的未定義行為 ## 參考資料 迭代器(iterator)和指针(pointer)区别在哪? https://www.zhihu.com/question/54047747 std::for_each https://en.cppreference.com/w/cpp/algorithm/for_each 能看清底層怎麼寫的 Writing a custom iterator in modern C++ https://www.internalpointers.com/post/writing-custom-iterators-modern-cpp Introduction to Iterators in C++ https://www.geeksforgeeks.org/introduction-iterators-c/ 帶著你拆開vector底層,看iterator怎麼做的 https://www.youtube.com/watch?v=F9eDv-YIOQ0&list=PLlrATfBNZ98dudnM48yfGUldqGD0S4FFb&index=94