C++初阶:STL详解(四)——vector迭代器失效问题

news2024/9/29 7:17:47

✨✨小新课堂开课了,欢迎欢迎~✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C++:由浅入深篇

小新的主页:编程版小新-CSDN博客

 一:迭代器失效的本质

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。

 二:迭代器失效的几种场景

对于vector可能会导致其迭代器失效的操作有:

以下的这几种场景也可以分成是由扩容引起的迭代器失效和非扩容引起的迭代器失效。

1. 引起其底层空间改变的操作(扩容)

这都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。

举个例子:

int main()
{
	vector<int> v{1,2,3,4,5,6};
	
    auto it = v.begin();
	while (it != v.end())
    {
	   cout << *it << " ";
	   ++it;
    } 

   cout << endl;
   return 0;

}

运行结果:

此时还是正常的,我们接着往下看。

void vector1()
{
	vector<int> v{ 1,2,3,4,5,6 };
	auto it = v.begin();

	// 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
	v.resize(100, 8);

	// reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变
	// v.reserve(100);

	// 插入元素期间,可能会引起扩容,而导致原空间被释放
	// v.insert(v.begin(), 0);
	// v.push_back(8);

	// 给vector重新赋值,可能会引起底层容量改变
	//v.assign(100, 8);

	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	
	
}

运行结果:

出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。 

2.指定位置元素的插入删除操作—insert / erase(非扩容)

insert:

void vector3()
{
	vector<int> v{ 1,2,3,4,5,6 };
	int x;
	cin >> x;
	auto p = find(v.begin(), v.end(), x);
	if (p != v.end())
	{
		// insert以后p就是失效,不要直接访问,要访问就要更新这个失效的迭代器的值
		v.insert(p, 20);
		(*p) *= 10;
	}

}

运行结果:

这里没有扩容,为什么insert还是导致迭代器失效了?其实我们p起初是指向2位置的迭代器,我们在p位置插入数据后,p就不指向2位置的迭代器了,虽然并没有因为扩容,使得p成为野指针,但是我们认为位置意义已经变了,insert之后迭代器也失效了,不要访问,如果非要访问,就更新这个失效的迭代器的值。

erase:

void vector2()
{
	int a [] = { 1,2,3,4,5,6 };

	vector<int> v(a, a + sizeof(a) / sizeof(int));
	// 使用find查找3所在位置的iterator
	vector<int>::iterator pos = find(v.begin(), v.end(), 3);

	// 删除pos位置的数据,导致pos迭代器失效。
	v.erase(pos);

	cout << *pos << endl; // 此处会导致非法访问
}

运行结果:

erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

后者的情况更好理解一点,前面那种情况我们可以类比insert来理解,删除指定位置上的元素后,位置意义已经变了,erase之后,迭代器也失效了。

3.其他环境下的迭代器失效

注意:Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端,一般迭代器失效也能运行,只不过运行结果会出错。要知道的是迭代器失效一定会导致结果错误,所有我们使用过程中要非常注意。

4.string的迭代器失效 

与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效。

void vector4()
{
	string s("hello world");
	auto it = s.begin();
	// 放开之后代码会崩溃,因为resize到20会string会进行扩容
	// 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了
	// 后序打印时,再访问it指向的空间程序就会崩溃
	s.resize(20, '!');
	while (it != s.end())
	{
		cout << *it;
		++it;
	} 
		cout << endl;
	it = s.begin();
	while (it != s.end())
	{
	// 按照下面方式写,运行时程序会崩溃,因为erase(it)之后
	// it位置的迭代器就失效了
	   s.erase(it);
	   ++it;
	}
}

运行结果:

三.迭代器失效的解决方法

万能钥匙:在使用前,对迭代器重新赋值即可。

示例一:

void vector01()
{
	vector<int> v{ 1,2,3,4,5,6 };
	auto it = v.begin();

	v.resize(100, 8);

    it = v.begin();//重新赋值
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;


}

运行结果:

示例二:

void vector02()
{
	vector<int> v{ 1,2,3,4,5,6 };
	int x;
	cin >> x;
	auto p = find(v.begin(), v.end(), x);
	if (p != v.end())
	{
		// insert以后p就是失效,不要直接访问,要访问就要更新这个失效的迭代器的值
		p = v.insert(p, 20);//更新迭代器,给迭代器重新赋值
		(*p) *= 10;
	}

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

	cout << endl;
}

 运行结果:

示例三:

void vector03()
{
	int a[] = { 1,2,3,4,5,6 };

	vector<int> v(a, a + sizeof(a) / sizeof(int));
	// 使用find查找3所在位置的iterator
	vector<int>::iterator pos = find(v.begin(), v.end(), 3);

	// 删除pos位置的数据,对pos迭代器重新赋值,更新迭代器
	pos = v.erase(pos);

	cout << *pos << endl; 
}

 运行结果:

总结:

 以上就是对迭代器失效的常见情况分析和解决方案,迭代器失效的问题还挺容易出现的,我们在使用迭代器的时候要注意到这一点。理解了迭代器失效,我们在实现vector的底层的时候,就知道该如何规避这一块的问题了,后面我们就一起的模拟实现vector吧。

感谢大佬们的观看,创作不易,还请各位大佬支持~ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2168219.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VulnStack-红日靶机(二)

红日靶机二 环境搭建 只需要把虚拟机的 host-only&#xff08;仅主机&#xff09;网卡改为 10.10.10.0 网段&#xff0c;如下配置 把 NAT 网卡&#xff0c;改为 192.168.96.0 网段&#xff0c;如下 首先恢复到 v1.3 快照 让后点击放弃&#xff0c;放弃后再开机&#xff0c;用…

Shopify独立站运营必知必会:选品与防封技巧

独立站和第三方平台是目前最常见的跨境电商销售模式&#xff0c;相比于第三方平台&#xff0c;独立站的商家可以自己建站&#xff0c;自行决定运营模式和营销手段等策略&#xff0c;尤其是在准入门槛上&#xff0c;难度会更低&#xff0c;这些特点吸引了不少商家选择独立站开店…

电动车、电单车入梯数据集电动车进电梯检测识别(代码+教程+数据集)

数据集介绍 共有 5347 张图像和一一对应的标注文件 标注文件格式提供了两种&#xff0c;包括VOC格式的xml文件和YOLO格式的txt文件。 标注的对象共有以下几种&#xff1a; [‘Electric-bicycle’] 标注框的数量信息如下&#xff1a;&#xff08;标注时一般是用英文标的&am…

使用shardingsphere实现mysql数据库分片

在大数据时代&#xff0c;随着业务数据量的不断增长&#xff0c;单一的数据库往往难以承载大规模的数据处理需求。数据库分片&#xff08;Sharding&#xff09;是一种有效的数据库扩展技术&#xff0c;通过将数据分布到多个数据库实例上&#xff0c;提高系统的性能和可扩展性。…

图解Lamda使用场景

1.参考如下文章&#xff0c;讲的挺好的 深入浅出 C Lambda表达式&#xff1a;语法、特点和应用 &#xff08;请注意&#xff1a;此链接是本章节的原文&#xff09; 2. 什么是 Lambda表达式&#xff08;截取以上参考文章&#xff09; Lambda表达式是一种在被调用的位置或作为…

每日OJ题_牛客_NC40链表相加(二)_链表+高精度加法_C++_Java

目录 牛客_NC40链表相加&#xff08;二&#xff09;_链表高精度加法 题目解析 C代码 Java代码 牛客_NC40链表相加&#xff08;二&#xff09;_链表高精度加法 链表相加(二)_牛客题霸_牛客网 题目解析 模拟⾼精度加法的过程&#xff0c;只不过是在链表中模拟。 C代码 /*…

107.WEB渗透测试-信息收集-FOFA语法(7)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;106.WEB渗透测试-信息收集-FOFA语法&#xff08;6&#xff09; 密码文件&#xff1a; 语…

MFC设置特定控件字体大小和背景颜色

MFC设置特定控件字体大小和背景颜色 初始化函数里 m_editFont.CreatePointFont(580 , _T("宋体"));m_ctrlEdit.SetFont(&m_editFont);重写消息 HBRUSH CMFCTESTDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {HBRUSH hbr CDialogEx::OnCtlColor(pDC,…

Calcite第一课

Calcite 是什么&#xff1f; 2024 年 9 月&#xff0c;最新版本 1.37.0 。前面三节我们先不看任何的源码&#xff0c;只从背景、介绍、概念、原理层面入手&#xff0c;作为深入学习和源码分析的预备。 如果用一句话形容 Calcite&#xff0c;Calcite 是一个用于优化异构数据源的…

最全测评!分享7款超好用的AI论文润色网站

在当前的学术写作领域&#xff0c;AI论文润色工具已经成为提升论文质量和效率的重要助手。以下是七款超好用的AI论文润色网站&#xff0c;其中特别推荐千笔-AIPassPaper。 一、千笔-AIPassPaper 是一款集论文大纲生成、内容填充、文献引用、查重修改于一体的全方位论文写作平…

友思特“未来视界”趣味实验室 | 第2讲:中草药的高光谱成像

第2讲 ——当中药遇上高光谱系统 友思特“未来视界”实验室第二讲开课了&#xff01;本期主角是凝结了中国古老智慧的特色——中药。 从神农尝百草到《本草纲目》典籍问世&#xff0c;中药材发展至今&#xff0c;品类已高达上万种。在这其中&#xff0c;难免会存在形态相似而…

曲面构件的布尔运算

1.前言 布尔运算算法有多种&#xff0c;可以根据几何数据表达方式分为Brep布尔运算、CSG布尔运算、网格布尔运算等&#xff0c;而网格布尔运算又又多种&#xff0c;如BSP方式、八叉树方式&#xff0c;博主实现过Brep布尔运算、BSP和八叉树两种网格布尔运算。详细可参考博主文章…

[NewStarCTF 2023 公开赛道]Begin of PHP1

开始代码审计. <?php error_reporting(0); highlight_file(__FILE__);if(isset($_GET[key1]) && isset($_GET[key2])){echo "Level 1<br>";if($_GET[key1] ! $_GET[key2] && md5($_GET[key1]) md5($_GET[key2])){$flag1 True;}else{die(…

依赖倒转原则(DIP)

依赖倒转原则&#xff08;DIP&#xff09;&#xff1a;Dependency Inversion Principle&#xff0c;模块之间要依赖抽象&#xff0c;不依赖实现&#xff0c;要面向接口编程&#xff0c;不要面向实现编程。高层次模块不应该直接依赖低层模块&#xff0c;这样就降低了客户端与实现…

2024.9.26 作业 +思维导图

一、作业 1、什么是虚函数&#xff1f;什么是纯虚函数 虚函数&#xff1a;函数前加关键字virtual&#xff0c;就定义为虚函数&#xff0c;虚函数能够被子类中相同函数名的函数重写 纯虚函数&#xff1a;把虚函数的函数体去掉然后加0&#xff1b;就能定义出一个纯虚函数。 2、基…

排序--堆排序【图文详解】

二叉树的相关概念 叶子&#xff1a;没有子节点的节点叫叶子节点 大根堆&#xff1a;所有的父亲大于儿子 小根堆&#xff1a;所有的儿子大于父亲 父亲于儿子的的下标关系&#xff1a; 父亲的下标为i &#xff0c;那么左孩子的下标为2*i1&#xff0c;右孩子的下标为2i2 子的下…

Fuxi:一款功能强大的跨平台渗透测试工具

关于Fuxi Fuxi是一款功能强大的跨平台渗透测试工具&#xff0c;该工具基于Python 3开发&#xff0c;支持在Linux、macOS和Windows操作系统上使用&#xff0c;具备良好的跨平台特性。在该工具的帮助下&#xff0c;广大研究人员可以轻松快速地执行渗透测试和安全研究任务。 工具…

SpringCloudEureka简介

背景 SpringCloudEureka是基于NetfliEureka做了二次封装&#xff0c;负责微服务架构的服务治理功能。 SpringCloud通过为Eureka增加SpringBoot风格的自动化配置&#xff0c;只需要简单的引入依赖和注解配置&#xff0c;就能让SpringBoot构建的微服务应用轻松和Eureka服务治理体…

SQL语言入门

一、SQL语言入门&#xff1a; 数据库管理人员&#xff08;DBA&#xff09;通过数据库管理系统&#xff08;DBMS&#xff09;可以对数据库&#xff08;DB&#xff09;中的数据进行操作 SQL是一种非过程化语言&#xff0c;只需提出“做什么”&#xff0c;而不需要指明“怎么做”…

11. LCEL:LangChain Expression Language

这篇文章覆盖了LCEL的理解和他是如何工作的。 LCEL(LangChain Expression Language)&#xff1a;是把一些有趣python概念抽象成一种格式&#xff0c;从而为构建LangChain组件链提供一种“简约”代码层。 LCEL在下面方面有着强大的支撑&#xff1a; 链的快速开发流式输出、异…