目录
前言
- 迭代器失效的主要原因:对容器进行插入、删除或内存重新分配的操作。
- 如何解决迭代器失效:
- 插入或删除操作后,重新获取迭代器。
- 遍历容器时删除元素,使用
erase()
的返回值获取下一个有效迭代器。- 使用
vector::reserve()
预先分配内存,减少插入操作导致的迭代器失效。- 删除
map
或set
元素时,依赖erase()
的返回值避免迭代器失效。
在使用容器(如 vector
, map
, set
, 等)进行操作时,迭代器失效(Iterator Invalidation)是一个常见问题。迭代器失效指的是:在对容器进行修改(如插入、删除、重分配)后,先前的迭代器、指针或引用可能不再有效,访问它们会导致未定义行为。
一、常见容器中的迭代器失效情况
-
vector
- 插入元素:
- 如果在容器中插入元素,并且导致
vector
重新分配内存(即容器容量不足,触发了动态扩展),则所有迭代器、指针、引用都会失效。 - 如果没有内存重新分配,仅仅插入到容器的中间或末尾,则插入位置后的迭代器失效。
- 如果在容器中插入元素,并且导致
- 删除元素:
- 当元素被删除时,被删除元素后的迭代器会失效。因为元素删除后,其后的元素会向前移动以填补空位。
- 插入元素:
-
deque
- 插入或删除元素:
- 插入或删除操作可能会导致容器的内存重新分配,导致所有迭代器失效。
- 插入或删除位于容器中间时,所有的迭代器都可能失效;但在两端插入时,只有两端的迭代器可能失效。
- 插入或删除元素:
-
list
/forward_list
- 插入元素:
- 插入元素不会使任何迭代器失效。因为
list
是链表结构,不涉及内存重新分配。
- 插入元素不会使任何迭代器失效。因为
- 删除元素:
- 删除元素后,只有指向被删除元素的迭代器失效,其他迭代器保持有效。
- 插入元素:
-
map
/set
/unordered_map
/unordered_set
- 插入元素:
- 插入新元素不会使现有的迭代器失效,因为这些容器底层采用平衡树或哈希表实现,插入时不会影响现有的元素位置。
- 删除元素:
- 删除元素时,指向被删除元素的迭代器会失效,但其他迭代器不会失效。
- 插入元素:
二、迭代器失效的常见场景及解决方法
1. 迭代器失效后继续使用导致未定义行为
- 场景:你对
vector
进行插入操作后,试图使用插入前的迭代器。 - 解决方法:
- 在插入或删除操作之后,重新获取有效的迭代器。
- 对于
vector
,在插入元素后需要更新或重新计算迭代器。例如:
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin();
vec.push_back(4); // 可能导致重新分配内存,失效之前的迭代器
// 重新获取迭代器
it = vec.begin();
2. 避免在循环中修改容器
- 场景:在遍历容器时同时删除元素,导致迭代器失效。
- 解决方法:
- 使用安全的迭代器操作,例如
erase()
函数在vector
或list
中返回一个指向下一个有效元素的迭代器。 - 使用迭代器返回的值避免使用失效的迭代器。
- 示例:
- 使用安全的迭代器操作,例如
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ) {
if (*it % 2 == 0) {
it = vec.erase(it); // erase 返回指向下一个有效元素的迭代器
} else {
++it;
}
}
3. 避免 vector
中频繁插入导致迭代器失效
- 场景:频繁插入
vector
导致内存重新分配,迭代器失效。 - 解决方法:
- 在使用
push_back()
等插入操作前,使用reserve()
预分配足够的内存以避免重新分配。 - 示例:
- 在使用
std::vector<int> vec;
vec.reserve(100); // 预分配内存,减少插入时的重新分配
for (int i = 0; i < 100; ++i) {
vec.push_back(i); // 不会导致迭代器失效
}
4. 在 map
或 set
中删除元素时避免迭代器失效
- 场景:在
map
或set
中删除元素时,指向被删除元素的迭代器失效。 - 解决方法:
- 使用
erase()
返回值来获取下一个有效的迭代器。 - 示例:
- 使用
std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}};
for (auto it = myMap.begin(); it != myMap.end(); ) {
if (it->first == 2) {
it = myMap.erase(it); // erase 返回下一个有效迭代器
} else {
++it;
}
}