【C++】 vector 迭代器失效问题

news/2024/9/27 23:33:06 标签: c++, 开发语言, 青少年编程

【C++】 vector 迭代器失效问题

  • 一. 迭代器失效问题分析
  • 二. 对于vector可能会导致其迭代器失效的操作有:
      • 1. 会引起其底层空间改变的操作,都有可能是迭代器失效
      • 2. 指定位置元素的删除操作--erase
      • 3. Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。
      • 4. 与vector类似,string在 插入或 扩容操作 或 erase之后,迭代器也会失效

一. 迭代器失效问题分析

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

注意1:迭代器失效后,代码并不一定会崩溃,但是运行结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的。

注意2:vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。 “ 其做法是,分配一个新的数组,然后将全部元素移到这个数组 ”。 但是原来定义的的迭代器未作处理依旧指向原来的地址,这就是导致迭代器失效的原因。
也就是说:一旦扩容就会导致迭代器失效。

迭代器失效解决办法:在使用前,对迭代器重新赋值即可

在这里插入图片描述
看图分析: 一旦经过扩容后原来的迭代器指针 it 不可在用,因为它还指向原来的旧空间,旧空间会被释放,旧空间释放后 it 就会变为野指针,需要重新更新迭代器即 newit

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

1. 会引起其底层空间改变的操作,都有可能是迭代器失效

比如:resize、reserve、insert、assign、push_back等。

#include <iostream>
using namespace std;
#include <vector>
int main()
{
	vector<int> v{ 1,2,3,4,5,6 };

	auto it = v.begin();

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

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

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

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

出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。
解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新赋值即可。

2. 指定位置元素的删除操作–erase

#include <iostream>
using namespace std;
#include <vector>
int main()
{
	int a[] = { 1, 2, 3, 4 };
	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; // 此处会导致非法访问
	return 0;
}

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

3. Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。

#include <iostream>
using namespace std;
#include <vector>
// 1. 扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
int main()
{
	vector<int> v{ 1,2,3,4,5 };

	for (size_t i = 0; i < v.size(); ++i)
		cout << v[i] << " ";
	cout << endl;

	auto it = v.begin();
	cout << "扩容之前,vector的容量为: " << v.capacity() << endl;

	// 通过reserve将底层空间设置为100,目的是为了让vector的迭代器失效 
	v.reserve(100);
	cout << "扩容之后,vector的容量为: " << v.capacity() << endl;

	// 经过上述reserve之后,it迭代器肯定会失效,在vs下程序就直接崩溃了,但是linux下不会
	// 虽然可能运行,但是输出的结果是不对的

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

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


http://www.niftyadmin.cn/n/5679821.html

相关文章

设备管理系统-TPM(PC+APP/PDA全流程)高保真Axure原型 源文件分享

随着科技的不断发展&#xff0c;企业对于设备管理的需求也日益增强。为了满足企业在设备管理方面的各种需求&#xff0c;站长为大家整理了一套设备管理系统TPM&#xff08;PCAPP/PDA全流程&#xff09;高保真Axure原型&#xff0c;通过这套原型&#xff0c;企业能够实现对设备的…

掌握Spring Boot数据库集成:用JPA和Hibernate构建高效数据交互与版本控制

在现代应用开发中&#xff0c;数据库操作是核心环节之一。Spring Boot提供了简化数据库集成的强大工具&#xff0c;而JPA&#xff08;Java Persistence API&#xff09;和Hibernate是两种非常流行的ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;可以帮助我们将对象…

[数据结构] 二叉树题目 (二)

目录 一. 另一颗树的子树 1.1 题目 1.2 示例 1.3 分析 1.4 解决 二. 平衡二叉树 2.1 题目 2.2 示例 2.3 分析 2.4 解决 三. 二叉树的遍历和创建 3.1 题目 3.2 示例 3.3 解决 一. 另一颗树的子树572. 另一棵树的子树 - 力扣&#xff08;LeetCode&#xff09; 1.1…

Webpack优化问题

目录 打包流程swcthread-loaderhash升级插件 打包流程 webpack 的打包流程大致可以分为以下几个步骤&#xff1a; 初始化&#xff1a;webpack 通过配置文件和 Shell 参数&#xff0c;初始化参数&#xff0c;确定入口文件、输出路径、加 载器、插件等信息。接下来读取配置文件…

React学习笔记(3.0)

classnames优化类名控制 classnames是一个简单的JS库&#xff0c;可以非常方便的通过条件动态控制class类名的显示。 安装classnames&#xff1a; npm i classnames 导入&#xff1a; import classNames from classnames <div className{classNames(box3,{box2:11})}&g…

0基础学前端 day2

大家好&#xff0c;欢迎来到无限大的频道。 今天继续带领大家开始0基础学前端。 一、CSS简介与基础 层叠样式表&#xff08;CSS&#xff0c;Cascading Style Sheets&#xff09;是用来进行网页样式和布局设计的语言。通过CSS&#xff0c;开发者可以控制网页中元素的颜色、字体…

栈及笔试题

目录 栈的实现 1、数组栈 2、链式栈 栈的创建 栈的打印 内存泄漏 栈溢出 练习 有效的括号 栈的实现 栈后入先出 1、数组栈 &#xff08;最佳实现&#xff0c;且访问数据的时候CPU告诉访存命中率比较高&#xff0c;因为地址连续存放&#xff0c;访问时CPU从cache里一…

三.python入门语法1

目录 1. 算数运算和关系运算 1.1. 算术运算符 1.2. 关系运算符 习题 2.赋值运算和逻辑运算 2.1. 赋值运算符 2.2. 逻辑运算符 3.位运算符 1&#xff09;位与运算&#xff08;A&B&#xff09; 2&#xff09;位或运算&#xff08;A|B&#xff09; 3&#xff09;异或位…