vector/list迭代器失效

什么是迭代器失效

迭代器是一种面向对象的广义指针,用于指向容器中或流中的对象,可以看做是一种指向数据的指针。
迭代器失效时一种效果,由特定操作对容器进行操作,使迭代器不指向容器中的任何元素,或者使得迭代器指向的容器元素发生了变化。

vector扩容引发迭代器失效

在这里插入图片描述

vector.erase

void test_vector_erase()
{
	vector<int>d;
	d.push_back(2);
	d.push_back(3);
	d.push_back(4);

	d.push_back(67);
	d.push_back(8);

	d.push_back(99);
	d.push_back(2);
	d.push_back(6);
	d.push_back(10);
	cout << d.size() << ":" << d.capacity() << endl;
	auto pos = find(d.begin(),d.end(),4);
	if (pos != d.end())
		d.erase(pos);
	cout << d.size() << ":" << d.capacity() << endl;
	cout << *pos << endl;
	//删除pos位置的数据,此时pos这个迭代器指向的位置是被删除位置的下一个
	//位置,但是还是会报错,因为此时迭代器的意义变了

	

}
}

在erase之后迭代器指向的位置是被删除位置的下一个位置,迭代器的意义发生了变化,所以也会报错。

段错误:
段错误就是访问了不可访问的内存,运行时出现了segmentation fault的报错
产生原因:访问不存在的内存地址,访问系统保存的内存地址。访问只读的内存地址。空指针废弃(eg:malloc,new与free,delete释放后继续使用),堆栈溢出,内存越界等。

下面一段代码,删除数组中的偶数

void test_vector_erase1()
{
	vector<int>d;
	d.push_back(2);
	d.push_back(3);
	d.push_back(4);

	d.push_back(67);
	d.push_back(8);

	d.push_back(99);
	d.push_back(2);
	d.push_back(6);
	auto it = d.begin();
	while (it != d.end())
	{
		if (*it % 2 == 0)
			d.erase(it);
		it++;
	}

}

同理,删除之后,此时如果继续it++,那么就不知道加到哪去了
通过stl的源码可以发现
在这里插入图片描述
其返回的是删除位置后的下一个位置,为了避免这个错误,我们只需要这样改

while (it != d.end())
	{
		if (*it % 2 == 0)
			it = d.erase(it);
		else
			it++;
	}

总结:erase报错大部分都是迭代器的意义发生了变化,或者不在有效访问数据范围之内,一般不会使用缩容方案导致出现野指针,

vector删除数据,都不考虑缩容方案,因为缩容方案是开一个size()大小的空间,拷贝数据,释放旧空间,本质是时间换空间,而因为大部分关注的是时间效率,不会关注空间效率,所以不会这样做,但insert确不一样

vector.insert

void test_vector_insert()
{
	vector<int>d;
	d.push_back(2);
	d.push_back(3);
	d.push_back(4);

	cout << d.size() << ":" << d.capacity() << endl;

	auto pos = find(d.begin(), d.end(), 4);
	if (pos != d.end())
	{
		d.insert(pos, 20);
	}

	cout << *pos << endl;
	*pos = 88;
	cout << *pos << endl;

}

insert出现错误可能有两种:
1:与上述问题一样,此时迭代器的指向的意义发生了变化,此时指向的是被加入的元素
2:有可能会出现扩容
因为扩容就是新开辟一段空间,然后将原数据内容拷贝过去,然后释放旧空间,此时会出现野指针问题。

整体总结:
对于insert和erase造成迭代器失效问题,Linux(g++)平台检查的很佛系,基本依靠操作系统自身野指针越界检查机制,window下vs系列检查更严格,使用一些强制检查机制,仅仅是意义变了也可能会检查出来。

list迭代器失效

由上述可知,我们可以将迭代器理解成指针,迭代器失效即迭代器指向的节点无效,也就是指针指向的空间失效,可能是意义发生了变化,也可能是野指针问题。
由于list的底层结构位带头结点的双向循环链表,而list中的insert通过源码可知此时返回的是新加入元素的位置,所以,insert是不会导致迭代器失效,只有erase才会导致迭代器失效,但失效的仅仅是被删除的结点,其他还是好的,因为被删除之后,其指向的空间被系统回收了,我们只要针对这个点就可以了。

void test_list_iterator_erase()
{
	list<int>d;
	d.push_back(2);
	d.push_back(6);
	d.push_back(3);
	d.push_back(9);
	d.push_back(5);
	auto pos = find(d.begin(), d.end(), 3);
	while (pos != d.end())
		d.erase(pos++);

	for (auto e : d)
		cout << e << " ";
	cout << endl;

}

像上述代码一样就可以了,如果仅仅是

while (pos != d.end())
		{
		  d.erase(pos);
		  pos++}

此时会报错,因为erase之后pos的位置已经被系统回收了,pos++其实访问的是野指针,会报错!
而我们这样写是在删除之前就使得pos的值指向了下一个位置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天少点debug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值