C++自定義迭代器:介紹了【什麼時候需要用到自定義迭代器】和【如何實現自定義迭代器】。 ...
目錄
1. 什麼時候需要使用自定義的迭代器?
常見、基本的數組類型
-
對於常見、基本的數組類型,如:
int
、char
,我們可以簡單地使用下標來進行遍歷:int array[5] = {1,2,3,4,5}; // 方法1:使用下標遍歷 for (int ind = 0; ind < 5; ++ind) { cout << array[ind] << " "; }
也可以使用範圍來進行遍歷,達到和使用下標同樣的遍歷效果:
// 方法2:使用範圍遍歷 for (int &n: array) { cout << n << " "; }
STL 容器
-
對於 STL 容器來說,如:
vector
、list
,我們同樣也可以使用下標和範圍來進行遍歷:vector<int> vec = {1,2,3,4,5}; // 方法1:使用下標遍歷 for (int ind = 0; ind < vec.size(); ++ind) { cout << vec[ind] << " "; } // 方法2:使用範圍遍歷 for (int &n: vec) { cout << n << " "; }
除了以上兩種方法,STL 容器還可以使用迭代器(iterator)進行遍歷:
vector<int>::iterator iter; // 方法3:使用迭代器遍歷 // .begin() 是指向 vector 首元素的位置的迭代器 // .end() 是指向 vector 最後一個元素的位置的下一個位置的迭代器 for (iter = vec.begin(); iter != vec.end(); ++iter) { cout << *iter << " "; }
自定義數據類型
-
下麵給出一個簡單的自定義類:
class Group { private: vector<vector<int>> students_marks; ...... }
其中,
students_marks
是一個用來記錄學生若幹次的分數的 vector,students_marks[0]
也是一個 vector,用來記錄第一個學生的分數,其中students_marks[0][0]
表示第一個學生的第一個分數。 -
如果要對
students_marks
進行遍歷,由於不存在vector<int>
類型的迭代器,因此方法2和方法3無法使用,只能使用下標遍曆法:// 定義一個函數,用來輸出 vector 中所有元素 void printvec(vector<int> &vec) { for(int ind = 0; ind < vec.size(); ++ind) { cout << vec[ind] << " "; } } // 使用下標對 vector<int> 類型的元素進行遍歷 for (int ind = 0; ind < students_marks.size(); ++ind) { printvec(students_marks[ind]); }
-
如果要使用以下方法對
students_marks
進行遍歷:// 定義 Group 類型的 G Group G; // 使用範圍遍歷 for(auto v: G) { printvec(v); }
則需要自己手動編寫指向
vector<int>
數據類型的迭代器。
2. 開始編寫自定義迭代器之前需要思考的問題
- 迭代器進行遍歷的對象是什麼?
- 迭代器遍歷的範圍是什麼?
- 迭代器指向的數據是什麼類型?
只要想明白這三個問題,就不難實現自定義類型的迭代器。
以上面的類 Group
為例,
我們想遍歷的是 students_marks
,將每個學生的分數輸出。
對應問題1,迭代器遍歷的對象是 vector<vector<int>> students_marks
。
對應問題2,假設總共有 k 個學生,則 students_marks.size() = k
,遍歷的範圍是從 students_marks[0]
到 students_marks[k-1]
。
對應問題3,迭代器指向的數據是 students_marks[x]
,類型是 vector<int>
。
3. 如何編寫及實現自定義類型的迭代器?
在後面的步驟中,一些常用的名詞將會以以下標識符代稱:
代替的標識符 | 對應上面的例子是 | |
---|---|---|
遍歷的對象 | OBJECT | students_marks |
遍歷對象的數據類型 | OBJECT_type | vector<vector<int>> |
遍歷對象所處類 | OBJECT_class | Group |
迭代器指向的數據 | VALUE | students_marks[x] |
迭代器指向的數據類型 | VALUE_type | vector<int> |
-
在
object
所處的class
中定義一個迭代器(iterator)的結構體(struct):class OBJECT_class { private: OBJECT_type OBJECT; public: struct Iterator { } ...... }
-
設定迭代器的屬性:
struct Iterator { using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = VALUE_type; using reference = const VALUE_type&; using pointer = VALUE_type*; }
iterator_category
是迭代器的類型,如果是正常、基礎的情況下,選擇forward_iterator_tag
就可以了difference_type
選擇ptrdiff_t
- 其餘的
value_type
、reference
和pointer
都是自定義的,根據實際情況填寫
-
定義迭代器的成員變數、構造函數、基礎函數
struct Iterator { using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = VALUE_type; using reference = const VALUE_type&; using pointer = VALUE_type*; // 構造函數 Iterator(pointer p) :ptr(p) {} // 拷貝賦值函數 Iterator& operator=(const Iterator& it) { ptr = it.ptr; } // 等於運算符 bool operator==(const Iterator& it) const { return ptr == it.ptr; } // 不等於運算符 bool operator!=(const Iterator& it) const { return ptr != it.ptr; } // 首碼自加 Iterator& operator++() { ptr++; return *this; } // 尾碼自加 Iterator operator ++(int) { Iterator tmp = *this; ++(*this); return tmp; } // 首碼自減 Iterator& operator--() { ptr--; return *this; } // 尾碼自減 Iterator operator --(int) { Iterator tmp = *this; --(*this); return tmp; } // 取值運算 VALUE_type& operator*() { return *ptr; } private: // 定義一個指針 pointer ptr; }
- 以上的函數不是必要也不是完整的,如果不需要用到的可以不寫,額外需要用到的函數可以自行編寫。
- 但是在大多數情況下,上面這些函數基本上足矣。
-
設定遍歷的範圍:
class OBJECT_class { private: OBJECT_type OBJECT; public: struct Iterator { ...... ...... private: pointer ptr; } // 遍歷的第一個元素的位置 Iterator begin() { VALUE_type* head = &OBJECT[0]; return Iterator(head); } // 遍歷的最後一個元素的下一個位置 Iterator end() { VALUE_type* head = &OBJECT[0]; return Iterator(head + OBJECT.size()); } ...... }
head
是指向第一個 VALUE 的指針