一、迭代器失效
向容器添加或者删除元素可能会导致指向容器的指针、引用或者迭代器失效。使用已经失效的指针、引用或者迭代器将会导致程序出现异常,编码过程中一定要时刻注意迭代器失效的场景。
例如,以vector为例:
int main() {
vector<int> v{1, 2};
vector<int>::iterator it;
for (it = v.begin(); it != v.end(); it++) {
v.push_back(*it);
}
return 0;
}
执行以上代码会导致段错误:
原因:在循环中新增了元素,并且重新分配了内存空间,导致迭代器失效。使用已经失效的迭代器会导致程序出现段错误。
迭代器失效,主要有两个层面的意思:
- 无法通过迭代器++或--操作遍历整个stl容器,记作第一层失效。
- 无法通过迭代器存取迭代器所指向的内存,记作第二层失效。
二、失效场景
vector和string
如果增加或删除元素导致内存空间重新分配了,那么指向容器的迭代器都会失效(第二层失效)。如果存储空间未重新分配,指向删除元素之前的所有迭代器还有效(第一层失效),但是删除元素之后的所有迭代器都无效了(第二层失效)。
deque
插入到除首尾元素之外任何位置都会导致迭代器失效(第二层失效)。如果插入到首尾元素,迭代器会失效,但是指向已存在元素的指针和引用不失效(第一层失效)。
删除除首尾元素之外的元素,所有迭代器失效(第二层失效)。如果删除的是首尾元素,首前和尾后迭代器失效,其他元素的引用、指针和迭代器不会失效。
map和set
删除和添加元素会导致内部结构调整,迭代器失效,但是引用和指针任然有效(第一层失效)。
list
添加元素不会导致迭代器失效,但是删除元素会导致删除元素后面的所有迭代器失效(第一层失效)。list删除元素永远都会导致尾后迭代器失效(第二层失效)。
三、避免迭代器失效
避免迭代器失效的几种方法:
- 减小迭代器的使用范围,不保存迭代器的值。
- 避免在遍历迭代器的过程中修改容器。
- 不要保存首前和尾后指针。
vector避免删除失效
在遍历vector的过程中删除元素,会导致后面的迭代器失效。如果希望删除后还能继续使用迭代器,要使用erase
方法,并接收返回值作为下一个迭代器使用。
正确的使用方式:
int main() {
vector<int> v{1, 2, 3, 4, 5};
vector<int>::iterator it;
for (it = v.begin(); it != v.end();) {
if (*it == 2 || *it == 4) {
// 接收返回值作为下一个迭代器
it = v.erase(it);
continue;
}
cout << *it << endl;
it++;
}
}
set/map避免迭代器失效
set和map也和vector一样:
int main() {
set<int> s{1, 2, 3, 4, 5};
set<int>::iterator it;
for (it = s.begin(); it != s.end();) {
if (*it == 2 || *it == 4) {
it = s.erase(it);
continue;
}
cout << *it << endl;
it++;
}
return 0;
}
此处评论已关闭