关于vector的迭代器失效

目录

关于迭代器失效的判定

1 迭代器指向的位置是野指针(全部迭代器失效)

原因:

解决

2 erase或者insert之后迭代器被更改了(部分迭代器失效)

原因

迭代器失效的场景:

改进之后

部分迭代器失效之越界访问

原因

 解决

关于平台问题

总结


关于迭代器失效的判定

(在windows平台下)

1 迭代器指向的位置是野指针(全部迭代器失效)

原因:

容量的改变导致空间的重新分配

比如insert或者push_back这种往原先的vector中插入元素的操作,会进行一个容量的判断,如果容量满了就会扩容:扩容是深拷贝,扩容(reserve)的具体操作是这样子的

① 先判断当前空前和reserve之后空间的大小,如果reserve之后的空间大于现在的capacity会扩容

②新开辟一块reserve之后大小的tmp的空间

③将原先空间的值拷贝到新的空间

④将原先的空间释放

因此如果不对迭代器进行更新的话,迭代器仍然指向的是之前被释放的空间,会导致迭代器失效

图解

 

如果erase采取以时间换空间的做法:缩容

这种导致容量的改变的,并且迭代器没有及时更新的,就会导致对应的迭代器失效

解决

这种由于容量的改变引起的,只能在设计reserve的时候,同时更新迭代器的值才可以

void reserve(size_t newcapacity)
	{
		if (newcapacity > capacity())
			size_t sz = size();
			T* tmp = new T[newcapacity];
			if (_start)
			{
				memcpy(tmp, _start, sizeof(T) * size());
				delete[]_start;
			}
			_start = tmp;
			_end = _start + sz;
			_endofstorage = _start + newcapacity;

		
	}

2 erase或者insert之后迭代器被更改了(部分迭代器失效)

原因

在erase或者insert之后capacity并没有发生改变的一个情况

erase的底层实现:erase当前位置之后的值都会往前移动一个位置并且返回在erase之前的下一个位置的迭代器的值

insert的底层实现:insert当前位置之后的值都会往后移动一个位置,并且返回insert的pos位置的迭代器的值。 

不管是erase和insert和之前相比,pos和pos位置之后的迭代器都发生了变化,在windows下的vs判定下,就判定了对应的迭代器失效了。

 

 他是这样判定的:原先每个位置的迭代器分别指向1,2,3,4,5,6,7,8,在4的位置插入之后,原先4的位置变成了新插入的10,并且之后所有的元素都发生了移位,这样子的话,原先的迭代器指向的值就发生了改变,vs判定迭代器失效

erase也是同理的,erase 4位置的值之后,其后的元素会往前挪动覆盖,这样子的话,原先4的位置变成了5,并且之后的值都发生了改变,因此原先迭代器和之后的迭代器都失效了

解决:这种情况下,erase和insert的pos位置和之后的迭代器会失效,不去访问这些位置就可以了。

如果非得访问,记得更新一下迭代器的值

迭代器失效的场景:

#include<iostream>
#include<vector>
using namespace std;

int main()
{
	int ar[] = { 1,2,3,4,0,5,6,7,8,9 };
	int n = sizeof(ar) / sizeof(int);
	vector<int> v(ar, ar + n);
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it != 0)
			cout << *it;
		else
			v.erase(it);

		it++;
	}
	return 0;
}
	

改进之后

 

部分迭代器失效之越界访问

删除的值在这一组值的末尾

原因

为什么会导致迭代器失效呢?

最根本的原因是迭代器erase掉最后一个值之后返回的是最后一个元素下一个位置的迭代器,而非最后一个元素!

这样子的话返回的位置是没有元素的,属于非法访问。导致了迭代器的失效

 解决

如何避免呢?

可以将返回的迭代器重新赋值,将末尾的元素给它

 

关于平台问题

在windows的vs下,和在Linux下对比,由于底层实现和检查机制的不同,因此对于上述情况的运行是否报错也不尽相同

上述的情况都是在windows下进行讨论的,如果在Linux平台下的话,erase和insert一个位置,并且如果不是末尾的元素或者中间出现的连续的偶数,并且不引起底层空间的改变的情况下,是不报错的

如果是末尾的元素,和vs下的情况一样,也是属于一个非法访问,是会报错的

如果是删除连续的数字的话,结果是不太一样的,vs下是会直接报错,但是Linux下的话,运行是没问题的,就是结果会出错,会只能删除一个对应的元素,因为返回的是删除元素的下一个位置的迭代器,这样子的话,如果正常情况下迭代进行访问,会直接错过这个位置的值(不更新迭代器的话)

总结:

迭代器失效最根本是因为指向原先位置的迭代器在erase和insert的作用下发生了改变。但是对于不同的场景迭代器失效的原因也不尽相同:

涉及到容量的改变引起的全部迭代器失效

涉及到erase和insert的部分迭代器失效,erase的值如果在末尾的话的一种特殊的情况

避免迭代器失效的做法也差不多,就是重新更改迭代器的值,让他指向正确的值。

因此如果要避免迭代器失效的话,我们需要对reserve,insert和erase的底层实现非常了解才可以采取对应的措施。

并且在不同的平台下,迭代器是否失效以及失效带来的影响也是不同的。

所以我们平时对迭代器的使用,如果无法判断的话,最好就不要去访问删除之后的迭代器了,或者如果要解决迭代器失效的问题的话,一定要注意更新!

 

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值