10.5 iterator:指向容器裡的位置
10.2 教的 push_back 能加元素、pop_back 能移除元素,但動得到的都只有尾端。想在中間插入或移除元素,得先回答一個問題:怎麼跟 vector 說「就是這個位置」?C++ 容器的標準答案是 iterator:一種特殊的型態,它的值是「容器裡的某個位置」。有點像上冊 7.7 的「陣列名稱代表開頭的記憶體位置」——iterator 存的不是元素本身,而是元素住在哪。
vector 的 iterator 型態寫作 vector<int>::iterator——中間的兩個冒號 :: 表示「vector<int> 裡面的 iterator 型態」:每種容器都帶著自己的 iterator 型態,所以要這樣指名。兩個最重要的來源:
v.begin():指向第一個元素的 iterator。v.end():指向最後一個元素的下一格——一個不裝資料的「結束位置」。像 8.6 的'\0'一樣,它本身不是內容,只負責標示「到此為止」。但有個關鍵差別:'\0'是陣列裡真的讀得到的一個字元,end()指的位置沒有元素——*v.end()是錯的(UB),end()只能當「邊界」用,不能當「最後一個元素」用。順帶一提,空 vector 的begin()就等於end()。
拿到 iterator 之後,*it 取出它指向位置的值(這個 * 唸作解參考(dereference),跟乘法無關),it++ 往後移一格:
¶範例程式碼
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {10, 20, 30, 40};
vector<int>::iterator it = v.begin(); // it 指向第一個元素
cout << *it << '\n'; // * 取出 it 指向位置的值
it++; // 往後移一格
cout << *it << '\n';
it += 2; // vector 的 iterator 可以一次跳好幾格
cout << *it << '\n';
cout << v.end() - v.begin() << '\n'; // 頭尾距離恰是元素個數
return 0;
}
執行結果:
10
20
40
4
最後一行值得咀嚼:begin() 到 end() 是「含頭不含尾」的一段,所以兩者的距離恰好是元素個數。順帶一提,「一次跳好幾格」(it += 2、v.begin() + i)是 vector 這類容器的 iterator 才有的待遇,不是每種容器都行——Level 0 只用 vector 和 string,放心用。而「從 begin() 到 end() 指定一整段」這個講法,正是下一單元 sort(v.begin(), v.end()) 的語言——那也是 iterator 在 Level 0 最重要的出場。
¶insert 與 erase
有了「位置」的語言,就能在任意位置動手:
| 寫法 | 功能 | 快慢 |
|---|---|---|
v.insert(it, x) |
在 it 指的位置插入 x(原本該處起的元素整段右移) |
O(n) |
v.erase(it) |
移除 it 指的元素(後面的元素整段左移) |
O(n) |
搭配 v.begin() + i 就是「索引 i 的位置」:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {10, 20, 30};
v.insert(v.begin() + 1, 99); // 在索引 1 的位置插入 99:{10, 99, 20, 30}
v.erase(v.begin() + 3); // 移除索引 3 的元素:{10, 99, 20}
for (int i = 0; i < 3; i++) {
cout << v[i] << '\n';
}
return 0;
}
執行結果:
10
99
20