Skip to main content

12-2 Iterator 的基本使用

一、取得 Iterator

函式說明
v.begin()指向第一個元素
v.end()指向最後一個元素的下一位(不可解參考!)
v.rbegin()反向,指向最後一個元素
v.rend()反向,指向第一個元素的前一位

 v.end() 不是最後一個元素,是「越界哨兵(Sentry)」,只能用來判斷是否走完,不能 *v.end()!

vector<int> v = {10, 20, 30, 40, 50};

vector<int>::iterator it;   // 宣告 iterator
it = v.begin();             // 指向第一個元素

cout << *it << "\n";        // 取值:10
++it;                       // 向前移一位
cout << *it << "\n";        // 20
it += 2;                    // 向前移兩位(vector iterator 支援)
cout << *it << "\n";        // 40

二、使用 Iterator 走訪 vector

方法一:傳統 iterator 寫法

缺點是 iterator 的宣告很長,撰寫或閱讀都會很困擾。

for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
    cout << *it << " ";
}
// 輸出:10 20 30 40 50

方法二:auto(C++11 起推薦)

自 C++11 起,auto 可以幫我們自動推導變數的型別。以下面程式碼為例,若 v 的型別是 vector<int>,則由 v.begin() 可以推導出 it 的型別為 vector<int>::iterator,這讓我們輕鬆不少。

for (auto it = v.begin(); it != v.end(); ++it) {
    cout << *it << " ";
}

方法三:範圍式 for(底層也是 iterator)

auto 和 範圍式for(range-based for loop) 在競程中都很常使用,因為非常簡潔方便。

for (int x : v) {
    cout << x << " ";
}

學 iterator 時建議先練習方法一,理解底層原理

三、反向走訪

vector 還有一組 r開頭的 iterator,rbegin()rend()。可以用來反向走訪。

vector<int> v = {10, 20, 30, 40, 50};
for (auto it = v.rbegin(); it != v.rend(); ++it) {
    cout << *it << " ";
}
// 輸出:50 40 30 20 10

四、Iterator 的算術運算

由於 vector 是具備隨機存取特性的容器,其 iterator 屬於隨機存取迭代器(Random Access Iterator),支援加減法運算。

vector<int> v = {10, 20, 30, 40, 50};
auto it = v.begin();

auto it2 = it + 3;          // 指向第 4 個元素
cout << *it2 << "\n";       // 40

cout << it2 - it << "\n";   // 兩個 iterator 的距離:3

並非所有容器都支援 +、-。像 list 的 iterator 就只能 ++、--。這是 vector 的優勢之一。

五、修改元素

Iterator 不只能讀,也能寫:

vector<int> v = {1, 2, 3, 4, 5};
for (auto it = v.begin(); it != v.end(); ++it) {
    *it *= 2;   // 每個元素乘以 2
}
// v 變成 {2, 4, 6, 8, 10}

容器的 iterator 通常還會有一組配套的 const iterator,用來確保我們在走訪的過程中只能讀取,無法修改容器內的元素。

begin()end() 配套的是 cbegin()cend()

rbegin()rend() 配套的是 crbegin()crend()

若程式碼嘗試使用 const iterator 來修改容器內的元素,會被編譯器擋下來。

for (auto it = v.cbegin(); it != v.cend(); ++it) {
    // *it = 0;  // 編譯錯誤!
    cout << *it << " ";
}