目錄
1. 什么是迭代器失效?
2. 哪些操作會導致迭代器失效?
2.1?vector?的插入操作(push_back,?insert)
示例:push_back?導致迭代器失效
如何避免?
2.2?vector?的刪除操作(erase,?pop_back)
示例:erase?導致迭代器失效
如何正確刪除?
3. 其他容器的迭代器失效情況
4. 總結
1. 什么是迭代器失效?
在 C++ 中,迭代器(iterator) 是一種類似指針的對象,用于遍歷 STL 容器(如 vector
、list
、map
等)。
迭代器失效 是指在對容器進行某些操作(如插入、刪除)后,原本有效的迭代器變得不可用,繼續使用它會導致 未定義行為(Undefined Behavior, UB),如程序崩潰、數據錯誤等
2. 哪些操作會導致迭代器失效?
不同的容器有不同的迭代器失效規則,本文主要討論 vector
的迭代器失效問題。
2.1?vector
?的插入操作(push_back
,?insert
)
當向 vector
插入元素時:
- 如果?
size() == capacity()
(容量已滿):vector
?會重新分配更大的內存,并拷貝原有數據。- 所有迭代器失效(包括?
begin()
,?end()
?等)。
- 如果?
size() < capacity()
(容量未滿):- 插入點之前的迭代器仍然有效。
- 插入點及之后的迭代器失效(因為元素可能被移動)。
示例:push_back
?導致迭代器失效
vector<int> v = {1, 2, 3};
auto it = v.begin(); // it 指向 1
v.push_back(4); // 可能觸發重新分配內存
cout << *it; // ? 危險!it 可能失效
如何避免?
- 提前預留空間(
reserve()
):
vector<int> v;
v.reserve(100); // 預留 100 個元素的空間
auto it = v.begin();
v.push_back(1); // 不會重新分配,it 仍然有效
- 使用索引代替迭代器(如果允許)。
2.2?vector
?的刪除操作(erase
,?pop_back
)
當從 vector
刪除元素時:
- 被刪除元素的迭代器失效。
- 被刪除元素之后的所有迭代器失效(因為后面的元素會向前移動)。
- 刪除點之前的迭代器仍然有效。
示例:erase
?導致迭代器失效
vector<int> v = {1, 2, 3, 4};
auto it = v.begin() + 2; // it 指向 3
v.erase(v.begin() + 1); // 刪除 2
cout << *it; // ? 危險!it 已經失效(3 已經前移)
如何正確刪除?
- 使用?
erase
?的返回值(返回下一個有效迭代器):
vector<int> v = {1, 2, 3, 4};
auto it = v.begin();
while (it != v.end()) {if (*it % 2 == 0) {it = v.erase(it); // 刪除并更新 it} else {it++; // 否則正常遞增}
}
反向遍歷(避免迭代器失效)
for (auto it = v.rbegin(); it != v.rend(); ) {if (*it % 2 == 0) {it = vector<int>::reverse_iterator(v.erase(it.base() - 1));} else {it++;}
}
3. 其他容器的迭代器失效情況
容器 | 插入操作(insert ) | 刪除操作(erase ) |
---|---|---|
vector | 可能失效(取決于容量) | 被刪除及后面的失效 |
deque | 可能失效(首尾安全) | 被刪除及附近的失效 |
list | 不會失效 | 僅被刪除的失效 |
map /set | 不會失效 | 僅被刪除的失效 |
4. 總結
vector
?插入時:- 可能失效(如果觸發重新分配)。
- 避免方法:提前?
reserve()
?或使用索引。
vector
?刪除時:- 被刪除及后面的迭代器失效。
- 正確做法:使用?
erase
?返回值或反向遍歷。
- 其他容器(如?
list
、map
)通常更安全,但仍需謹慎。
最佳實踐:
- 避免在遍歷時直接修改容器,除非明確知道迭代器是否有效。
- 盡量使用?
range-based for
?或算法(如?remove_if
),減少手動管理迭代器。 - 調試時使用?
-D_GLIBCXX_DEBUG
(GCC)檢測迭代器錯誤。