目录
1.什么是迭代器失效?
(1)文字阐述迭代器失效
(2)举例阐述迭代器失效
2.vector哪些操作会导致迭代器失效?
3.如何避免迭代器失效?
在上一篇关于vector用法的博客中,曾提到过迭代器失效这个话题,但是因为那篇文章主要是用来展示函数用法的,所以单独来谈一下迭代器失效。
这篇文章的要点只有三点:1.什么是迭代器失效? 2.vector哪些操作会导致迭代器失效? 3.如何避免迭代器失效?
[1]迭代器的本质就是指针,迭代器失效就是指针失效。
[2]指针失效:指针指向的空间是非法的。
[3]指针指向非法空间:指向了被释放的空间 或者 越界访问 。
是不是很简单,只需要三句话就可以简单阐述迭代器失效。
下面来看一个例子更好的理解迭代器失效。
代码一:这段代码编译的时候就已经报错了,所以为大家截图了调试时的监视窗口。
原因:it指向初始空间的首元素,尾插元素时空间不足,开辟更大的新空间存储元素,释放旧空间,但此时it仍旧指向已经被释放的旧空间,成为了野指针。在循环中试图对野指针进行解引用导致程序崩溃。
//代码一
#include "iostream"
#include "vector"
using namespace std;
void Test() {
vector v{ 1,2,3,4 };
//指向当前空间的首元素
auto it = v.begin();
//开辟新空间,拷贝元素释放旧空间
//此时it仍旧指向旧空间
v.push_back(5);
while (it != v.end()) {
cout << *it;
it++;
}
}
int main() {
Test();
}
调试代码时的 监视窗口:在监视窗口中可以看到,初始容量是4,尾插时进行了扩容,这也就导致了it成为野指针。
相信通过这个例子大家已经更好的理解了迭代器失效。
a.所有可能会引起扩容的操作都可能会导致迭代器失效。如:resize、reserve、insert、assign、push_back等
这一点在举例子时已经证明过了,这里就不再赘述了。
b.指定位置的删除操作--erase。这一点比较难理解。
首先要知道erase(iterator pos)函数的返回值是被删除元素后一位元素的迭代器。当删除元素后,pos位置之后的元素会往前搬移,没有导致底层空间改变。理论上来说是不是觉得这个函数并不会导致迭代器失效?
但是:如果pos元素刚好是最后一个元素,删完后pos就刚好是end()的位置,而end()位置并没有元素,那么一旦试图解引用pos就会崩溃。因此vs编译器觉得这样的操作不安全,vs就认为当你使用erase删除了一个元素后,该位置的迭代器就应该按失效处理,不要再使用了。
//代码二
#include "iostream"
#include "vector"
#include "algorithm"
using namespace std;
void Test() {
vector v{ 1,2,3,4 ,5,6,7,8,9,10};
auto it = v.begin();
//删除偶数
while (it != v.end()) {
if (*it % 2 == 0)
v.erase(it);
else
it++;
}
}
int main() {
Test();
}
解决办法简单粗暴:在所有可能导致迭代器失效的操作之后,如果要使用迭代器,在使用前重新赋值,使其指向新空间。
既然迭代器可能失效,那么我就在使用前重新赋值,让其指向合法的空间再使用。
现在来把代码一和二做一点小小的改动,让它们变得安全。
代码三:将代码一和二放在同一段代码中,在使用之前对其重新赋值。
使用erase函数后,就用它的返回值重新对迭代器赋值,使它变得安全。
//代码三
#include "iostream"
#include "vector"
#include "algorithm"
using namespace std;
void Test1() {
vector v{ 1,2,3,4 };
auto it = v.begin();
v.push_back(5);
//使用迭代器前对它重新赋值
it = v.begin();
while (it != v.end()) {
cout << *it;
it++;
}
}
void Test2() {
vector v{ 1,2,3,4 ,5,6,7,8,9,10 };
auto it = v.begin();
//删除偶数
while (it != v.end()) {
if (*it % 2 == 0)
//因为erase函数返回值是被删除元素后一位元素的迭代器
//用它的返回值对it进行重新赋值
it = v.erase(it);
else
it++;
}
}
int main() {
Test1();
Test2();
}