目录
- 什么是迭代器失效
- 导致迭代器失效的操作
- VS和g++环境下对与迭代器失效的态度
什么是迭代器失效
迭代器的底层其实就是一个指针,或者对指针进行了封装
vector
的迭代器就是一个指针T*
一个迭代器指向某一个空间,此时这块空间被释放了,这个迭代器仍指向原来的那个空间,这个就叫迭代器的失效
如果继续使用已经失效的迭代器,程序可能会崩溃
导致迭代器失效的操作
引起底层空间发生变化的操作,都很有可能引起迭代器失效
迭代器失效,主要都是由insert
和erase
导致的
下面我们看一个insert
导致迭代器失效的情况
我们首先定义一个vector<int>
对象和一个迭代器
void test6()
{
vector<int> v{ 1,2,3,4,5,6 };
auto it = v.begin();
}
此时v的size()
的值和capacity()
的值都为6,如果再向v中插入数据,就需要扩容。
void test6()
{
vector<int> v{ 1,2,3,4,5,6 };
auto it = v.begin();
v.insert(it, 0);//此处会扩容
}
v.insert(it, 0)
会导致扩容,扩容其实就是另开辟一块更大空间,原空间销毁,所以此时迭代器it
已经失效了,因为后面没有再使用it
,所以暂时不会报错。
此处如果再使用已经失效的迭代器it
,就会报错
void test6()
{
vector<int> v{ 1,2,3,4,5,6 };
auto it = v.begin();
v.insert(it, 0);//此处会扩容
v.insert(it, 0);
}
所以想要迭代器不失效,就要为迭代器重新赋值
insert
函数在库中的定义为:
iterator insert (iterator position, const value_type& val)
可以看到它的返回值是iterator
类型的,这其实返回了一个处理后不失效的迭代器,是指向第一个新插入元素的迭代器。
void test6()
{
vector<int> v{ 1,2,3,4,5,6 };
auto it = v.begin();
it = v.insert(it, 0);//此处会扩容
v.insert(it, 10);//不会报错
}
下面看一下erase
导致失效的情况:
其实大多数情况下,使用erase
并不会导致迭代器
void test7()
{
vector<int> v{ 1,2,3,4,5,6 };
auto it = v.begin()+3;
v.erase(it);
}
这里迭代器it
指向的是4,v.erase(it)
删除4后,后面的元素会向前补上来,没有导致底层空间的改变,理论上讲迭代器不应该会失效
如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了
以及,如果vector中如果只有一个元素,迭代器恰好指向这个元素,那么erase
后,迭代就会失效
可以看出,erase虽然有的情况会导致迭代器失效,有点情况不会导致迭代器失效,但是在VS环境下就认为:删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。
其实与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效
VS和g++环境下对与迭代器失效的态度
- VS环境下,对于迭代器失效十分严格,只要有迭代器失效的情况,编译器就会报错;只要删除vector中任意位置上元素时,就认为该位置迭代器失效了,报错
- 但是linux上的g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。
- 扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
- erase删除任意位置代码后,linux下如果迭代器没有失效,程序还是会运行
- erase删除的迭代器如果是最后一个元素,删除之后it已经超过end,如果++it还是会报错的
- 所以迭代器失效后,代码并不一定会崩溃,但是运行结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的