C++中vector迭代器哪些情況下會失效?
在C++編程中,std::vector是一個非常強大的容器,用于存儲動態(tài)數(shù)組。然而,使用std::vector時需要注意的一個重要方面是其迭代器的行為。當vector的容量發(fā)生變化時(例如添加或刪除元素),迭代器可能會失效,這可能導(dǎo)致程序錯誤。
迭代器失效的情況:
1、重新分配內(nèi)存
當向vector添加新元素導(dǎo)致其容量不足時,vector會自動擴展以容納更多的元素。這種情況下,所有指向舊內(nèi)存區(qū)域的迭代器、指針和引用都會失效。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin(); // 指向第一個元素
// 添加一個元素可能觸發(fā)重新分配
vec.push_back(4);
if (it != vec.end()) {
std::cout << "Element: " << *it << std::endl;
} else {
std::cout << "Iterator is invalid." << std::endl;
}
return 0;
}
在這個例子中,如果push_back()操作觸發(fā)了重新分配,則it迭代器將變得無效,嘗試訪問它會導(dǎo)致未定義行為。
2、刪除元素
從vector中移除元素后,該位置之后的所有元素及其對應(yīng)的迭代器都會受到影響。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.erase(vec.begin()); // 刪除第一個元素
while (it != vec.end()) {
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
return 0;
}
這里,erase()返回的是下一個有效元素的迭代器,確保了循環(huán)的安全性。
3、使用 clear() 方法清空容器
當調(diào)用clear()時,所有元素都會被刪除,但已分配的內(nèi)存空間不會被釋放。這意味著容器的容量保持不變,但所有指向原元素的迭代器、指針或引用都將變得無效。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin(); // 獲取開始位置的迭代器
std::cout << "Before clear: Capacity = " << vec.capacity() << ", Size = " << vec.size() << std::endl;
vec.clear(); // 清空向量
std::cout << "After clear: Capacity = " << vec.capacity() << ", Size = " << vec.size() << std::endl;
if (it != vec.end()) {
// 這里會輸出錯誤信息,因為it已經(jīng)失效
std::cout << "Element: " << *it << std::endl;
} else {
std::cout << "Iterator is invalid after calling clear()." << std::endl;
}
return 0;
}
調(diào)用clear()后,迭代器it仍然指向原來的內(nèi)存位置,但由于這些位置上的元素已被刪除,所以it實際上是無效的。為了安全起見,應(yīng)該在clear()之后重新獲取迭代器。
4、使用 resize() 改變大小
減小尺寸:如果通過resize()減小vector的尺寸,那么超出新尺寸范圍的元素會被刪除,相應(yīng)的迭代器也會失效。
增大尺寸:如果增加尺寸,則新增加的位置上的迭代器是未定義的(直到它們被初始化)。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin() + 2; // 指向第三個元素
std::cout << "Before resize: Capacity = " << vec.capacity() << ", Size = " << vec.size() << std::endl;
std::cout << "Element at it before resize: " << *it << std::endl;
vec.resize(2); // 減小尺寸到2
std::cout << "After resizing to 2: Capacity = " << vec.capacity() << ", Size = " << vec.size() << std::endl;
if (it != vec.end()) {
// 這里可能會導(dǎo)致未定義行為,因為it已經(jīng)失效
std::cout << "Element at it after resize: " << *it << std::endl;
} else {
std::cout << "Iterator is invalid after resizing to 2." << std::endl;
}
vec.resize(6, 0); // 增大尺寸到6,并用0填充
it = vec.begin() + 2; // 重新獲取有效迭代器
std::cout << "After resizing to 6: Capacity = " << vec.capacity() << ", Size = " << vec.size() << std::endl;
std::cout << "Element at it after resizing to 6: " << *it << std::endl;
return 0;
}
5、使用 swap() 交換兩個 vector 的內(nèi)容
當兩個vector之間進行數(shù)據(jù)交換時,它們各自的迭代器、指針和引用都將指向?qū)Ψ降臄?shù)據(jù)結(jié)構(gòu)。這意味著原來的迭代器將不再指向原來的數(shù)據(jù)。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = {4, 5, 6};
auto it1 = vec1.begin();
auto it2 = vec2.begin();
std::cout << "Before swap: vec1[0] = " << *it1 << ", vec2[0] = " << *it2 << std::endl;
vec1.swap(vec2); // 交換兩個向量的內(nèi)容
std::cout << "After swap: vec1[0] = " << *it1 << ", vec2[0] = " << *it2 << std::endl;
// 輸出顯示迭代器現(xiàn)在指向了交換后的數(shù)據(jù)
std::cout << "it1 now points to: " << *it1 << std::endl;
std::cout << "it2 now points to: " << *it2 << std::endl;
return 0;
}
6、使用 assign() 重新賦值
assign()可以用來替換vector中的所有元素,這個過程類似于先調(diào)用clear()再插入新元素。因此,任何之前存在的迭代器都會失效。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin();
std::cout << "Before assign: Element at it = " << *it << std::endl;
vec.assign(3, 0); // 用3個0替換原有元素
if (it != vec.end()) {
// 這里會導(dǎo)致未定義行為,因為it已經(jīng)失效
std::cout << "After assign: Element at it = " << *it << std::endl;
} else {
std::cout << "Iterator is invalid after assign." << std::endl;
}
// 重新獲取有效的迭代器
it = vec.begin();
std::cout << "New element at it: " << *it << std::endl;
return 0;
}
vector刪除經(jīng)典錯誤
當從vector中刪除一個元素時,所有指向該元素及其之后元素的迭代器都會失效。如果繼續(xù)使用這些迭代器,會導(dǎo)致未定義行為。
下面這個是新手最容易犯的一個錯誤:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
if (*it == 3) {
vec.erase(it); // 刪除元素3
}
std::cout << *it << " "; // 繼續(xù)使用失效的迭代器
}
std::cout << std::endl;
return 0;
}
當*it == 3時,調(diào)用vec.erase(it)會刪除元素3。
erase()返回的是下一個有效元素的迭代器,但在這個例子中,it并沒有更新為新的迭代器,而是繼續(xù)遞增,導(dǎo)致訪問已失效的迭代器。
正確的做法是:
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ) {
if (*it == 3) {
it = vec.erase(it); // 更新迭代器
} else {
++it; // 只有在沒有刪除元素的情況下才遞增迭代器
}
}
for (const auto &val : vec) {
std::cout << val << " ";
}
std::cout << std::endl;
return 0;
}
總結(jié):
為了避免由于迭代器失效引起的錯誤,我們在使用迭代器遍歷vector時,要避免在可能使迭代器失效的操作前后更新迭代器,或者盡量減少對vector的修改操作直到遍歷完成。此外,使用基于范圍的for循環(huán)(如for (auto &elem : vec))可以避免直接管理迭代器,從而簡化代碼并減少出錯的機會。