先看下面的例子:
vector 的元素刪除
話頭從 container 的元素刪除說起。jyhuang 觀察到「如果 vector 或 list
的最後一個元素符合刪除條件,程式會有問題」。他給我這樣一個片段:
template <typename T>
void print_elements(T elem) { cout << elem << " "; }
void (*pfi)(int) = print_elements; // 函式指標,做為 function object 用
...
int ia[7] = {0,1,2,3,4,5,6};
vector<int> ivec(ia, ia+7);
for_each(ivec.begin(), ivec.end(), pfi); // 0 1 2 3 4 5 6
for (vector<int>::iterator it = ivec.begin(); it != ivec.end(); it++) {
if (*it % 2) // 把奇數去掉
ivec.erase(it);
}
for_each(ivec.begin(), ivec.end(), pfi); // 0 2 4 6 正確!
如果把 array 和 vector 的初值改為:
int ia[8] = {0,1,2,3,4,5,6,7};
vector<int> ivec(ia, ia+8);
則上述程式會當掉。
●I don't think so...
我不相信 STL 會有這麼差勁的表現。所以多做了幾次測試如下。
(1) 把 array 和 vector 的初值改為:
int ia[7] = {0,1,2,3,4,5,7};
vector<int> ivec(ia, ia+7);
程式結果為 0 2 4 7。結果不對,但不會當掉。
(2) 把 array 和 vector 的初值改為:
int ia[7] = {0,1,3,5,7,9,7};
vector<int> ivec(ia, ia+7);
程式結果為 0 3 7 7。錯得離譜,但不會當掉。
(3) 把 array 和 vector 的初值改為:
int ia[7] = {0,2,4,6,0,4,7};
vector<int> ivec(ia, ia+7);
程式執行時會當掉。
我於是多做了一些觀察,然後知道,上述這些動作根本上是完全
不對的。第一個 sample 之執行結果正確,完全是湊巧。
問題不在「最後一個元素是否符合刪除條件」,而在對 iterator
特性的認識。當我們做了 ivec.erase(it) 動作,vector 便動態
減縮了一個元素(邏輯上,後繼元素向前遞補),而 it 不變。
之後 for 迴圈的第三部份述句要求 it++,造成 it 跳過了一個元素...。
看實例:
(1) 初值為 {0,1,2,3,4,5,6}
第一次迭代,發現是偶數: ★ 以下以 e 表示 end()
0 1 2 3 4 5 6 e
^
第二次迭代,發現是奇數:
0 1 2 3 4 5 6 e
^
於是將 '1' 刪除,vector 變成:
0 2 3 4 5 6 e
^
第三次迭代,發現是奇數(這時已發生錯誤,因為 '2' 被跳過,沒有檢查):
0 2 3 4 5 6 e
^
於是將 '3' 刪除,vector 變成:
0 2 4 5 6 e
^
第四次迭代,發現是奇數(這時已發生錯誤,因為 '4' 被跳過,沒有檢查):
0 2 4 5 6 e
^
於是將 '5' 刪除,vector 變成:
0 2 4 6 e
^
第五次迭代,發現 it == vec.end(),於是跳離迴圈:
0 2 4 6 e
^
此時 vector 剩餘 0 2 4 6,陰錯陽差地與正確結果吻合。
(2) 初值為 {0,2,4,6,0,4,7}
第一次迭代,發現是偶數: ★ 以下以 e 表示 end()
0 2 4 6 0 4 7 e
^
第二次迭代,發現是偶數:
0 2 4 6 0 4 7 e
^
第三次迭代,發現是偶數:
0 2 4 6 0 4 7 e
^
第四次迭代,發現是偶數:
0 2 4 6 0 4 7 e
^
第五次迭代,發現是偶數:
0 2 4 6 0 4 7 e
^
第六次迭代,發現是偶數:
0 2 4 6 0 4 7 e
^
第七次迭代,發現是奇數:
0 2 4 6 0 4 7 e
^
於是將 '7' 刪除,vector 變成:
0 2 4 6 0 4 e
^
第八次迭代,it 越過了 end(),造成越界錯誤。迴圈何時結束,未可知也:
0 2 4 6 0 4 e ? ? ? ? ? ?
^
我的結論:
iterator 只適用於在「不更動 container 佈局」的條件下,對 container 做巡訪動作。否則,對 iterator 的取值行為,直觀下極易誤用。
本文探讨了在C++ STL中使用迭代器从vector容器删除元素时可能遇到的问题,并通过实例演示了错误操作导致的后果。
1002

被折叠的 条评论
为什么被折叠?



