[C++]迭代器失效问题

目录

一、迭代器失效案例

二、迭代器失效原因

2.1、内存重新分配导致迭代器失效

2.2、数据插入到错误位置

三、解决迭代器失效

四、总结

一、迭代器失效案例

接下来用vector的insert()功能举例

**在insert函数中,如果发生了扩容,那么pos迭代器会失效,因为reserve会重新分配内存,而pos仍然指向旧空间,**从而在后续的移动元素和赋值操作中出现问题。

在测试函数test_vector04中,输出结果:

第一次插入(v1.begin()+1, 200)成功,输出1 200 2 3

第二次插入(v1.begin()+1, 300)却得到了异常输出1 200 2 3 -842150451

这是因为第一次插入后,容器状态为[1,200,2,3],容量为4。第二次插入时,容器已满,需要扩容。

扩容后,pos(即v1.begin()+1)没有更新,指向了已释放的内存。然后在移动元素和赋值时使用了失效的迭代器,导致未定义行为。

复制代码
===这段代码主要展示迭代器失效问题和出现的现象,push_back函数功能是正常的,扩容机制是原来的空间*2
一开始分配的是4个空间,所以本代码重点演示在插入第5个元素的发生情况====
		void insert(iterator pos, const T& val)
		{
			//判断pos是否合法
			assert(pos>=_start&&pos <= _finish);

			//判断容量是否足够
			if (size() == capacity())
			{
				size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
				reserve(newcapacity);
			}
			iterator end = _finish-1;
			//iterator end1 = end();//最好不同名

			//注意如果执行了扩容reserve,此地方的pos需要更新,!!!
			//因为_start,_finsih,_endofstrogr都是更新过的,而pos指向的还是旧空间
			//否则出现迭代器失效情况,数据未插入到新空间,而是非法插入到了旧空间。
			while (end >= pos)
			{
				*(end + 1) = *end;
				end--;
			}
			*pos = val;
			_finish++;
		}
void test_vector04()
{
	hx::vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);

	for (auto ch : v1)
	{
		cout << ch << " ";
	}
	cout << endl;

	//在不扩容的情况下任意位置插入数据,插入此时容量是4,再插入会发生扩容
	v1.insert(v1.begin() + 1, 200);

	for (auto ch : v1)
	{
		cout << ch << " ";
	}
	cout << endl;

	//继续插入时迭代器失效
	v1.insert(v1.begin() + 1, 300);
	for (auto ch : v1)
	{
		cout << ch << " ";
	}
	cout << endl;
}
//运行结果
//1 2 3
//1 200 2 3
//1 200 2 3 -842150451 第二次插入时由于迭代器失效,导致结果异常(出现了随机值-842150451,这是未初始化内存的值

二、迭代器失效原因

2.1、内存重新分配导致迭代器失效

在调用reserve(newcapacity)时,如果需要内存重新分配,则会执行reserve函数:

  1. 分配一块新的内存空间(大小为newcapacity)。

  2. 将原有数据从旧内存拷贝到新内存。

  3. 释放旧内存。

在这个过程中,旧内存被释放,而原来的迭代器pos(包括_start_finish)都指向已经释放的内存,这些迭代器就失效了。

2.2、数据插入到错误位置

如果继续使用失效的 pos

  • *pos = val 会写入到已释放的内存

  • 可能导致程序崩溃或数据损坏

  • 新数据实际上没有插入到容器的有效空间中

三、解决迭代器失效

在更新成员变量的同时,更新pos,可以解决迭代器失效的问题,简单的理解就是,在扩容后,通过保存的偏移量来重新计算pos。然后在扩容后,用已经更新的_start加上这个偏移量得到新的pos。

复制代码
		void insert(iterator pos, const T& val)
		{
			//判断pos是否合法
			assert(pos>=_start&&pos <= _finish);

			size_t len = pos - _start;//=========此处记录pos和_start的相对位置len

			//判断容量是否足够
			if (size() == capacity())
			{
				size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();
				reserve(newcapacity);
			}
			iterator end = _finish-1;

			pos = _start + len;//==========通过更新后的_start+相对位置len更新pos

			//iterator end1 = end();//最好不同名

			//注意如果执行了扩容reserve,此地方的pos需要更新,!!!
			//因为_start,_finsih,_endofstrogr都是更新过的,而pos指向的还是旧空间
			//否则出现迭代器失效情况,数据未插入到新空间,而是非法插入到了旧空间,
			while (end >= pos)
			{
				*(end + 1) = *end;
				end--;
			}
			*pos = val;
			_finish++;
		}
//输出结果
//1 2 3
//1 200 2 3
//1 300 200 2 3

四、总结

迭代器失效原因:是内存重分配使迭代器失效,将数据写入到已经释放的空间。

解决办法:需要通过偏移量重新计算位置。

相关推荐
前端不太难27 分钟前
《Vue 项目路由 + Layout 的最佳实践》
前端·javascript·vue.js
apocelipes30 分钟前
从源码角度解析C++20新特性如何简化线程超时取消
c++·性能优化·golang·并发·c++20·linux编程
LYFlied30 分钟前
【每日算法】 LeetCode 56. 合并区间
前端·算法·leetcode·面试·职场和发展
ozyzo31 分钟前
求1~n的累加和
c++
想学后端的前端工程师1 小时前
【Vue3组合式API实战指南:告别Options API的烦恼】
前端·javascript·vue.js
否子戈1 小时前
WebCut前端视频编辑UI框架一周开源进度
前端·音视频开发·ui kit
昔人'1 小时前
`corepack` 安装pnpm
前端·pnpm·node·corepack
萌萌哒草头将军1 小时前
pnpm + monorepo 才是 AI 协同开发的最佳方案!🚀🚀🚀
前端·react.js·ai编程
charlie1145141912 小时前
现代C++嵌入式教程:C++98基础特性:从C到C++的演进(1)
c语言·开发语言·c++·笔记·学习·教程
历程里程碑2 小时前
C++ 18智能指针:告别内存泄漏的利器
开发语言·c++