【C++】-vector的具体使用(迭代器失效问题)

news2024/11/17 3:46:15

请添加图片描述
💖作者:小树苗渴望变成参天大树🎈
🎉作者宣言:认真写好每一篇博客💤
🎊作者gitee:gitee✨
💞作者专栏:C语言,数据结构初阶,Linux,C++ 动态规划算法🎄
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!

文章目录

  • 前言
  • 一、文档介绍[vector](https://legacy.cplusplus.com/reference/vector/vector/?kw=vector)
    • 1.1vector的接口
    • 1.2vector构造器
    • 1.3vector遍历和修改数据操作
    • 1.4vector的空间增长问题
    • 1.5vector的插入和删除操作
    • 1.6vector的排序操作
    • 1.7vector的查找操作(迭代器失效)
  • 二、总结


前言

今天我们开始讲解新的知识,关于vector的使用,这个容器使用起来非常的香,他可以做很多事情,也类似于一种数组的实现,但是他比数组更加的高级,让我们一起来看看vector是怎么使用的吧


一、文档介绍vector

在这里插入图片描述
对于vector我们目前可以简单理解为存放任意类型的可变数组。也可以像数组那些去使用,但是又被数组的用法多。

1.1vector的接口

想要学会vector的使用,我们需要熟悉它的接口,他能更好的运用它,接下来我就带大家来学习再常用的接口以及使用方法
在这里插入图片描述
通过文档我们发现vector实现了这些的接口,有些接口大家看到应该就东什么意思,因为我们刚刚学会string类,接口名都是一样的,我们想要使用一个容器,就必须先实例化出对象,就要先掌握它对应的构造器

1.2vector构造器

在这里插入图片描述
来看看这几种构造器的使用:

	//1.默认构造器
	vector<int> v1;
	
	//2.初始化的值要符合定义的类型
	vector<int> v2(4,100);
	
	//3.使用迭代器或者指针都行
	string s;
	int a[4] = { 0 };
	vector<int> v3(s.begin(), s.end());
	vector<int> V3(a,a+4);
	
	//4.用另一个vector对象创建另一个
	vector<int> v4(v3);

我们一共就这四种,再下篇的模拟实现中,我会带大家都实现一遍,让大家体会里面的原理

1.3vector遍历和修改数据操作

(1)iterator
在这里插入图片描述
我们迭代器的种类非常多,博主就介绍其中一种,其余的使用是类似的,这个迭代器的使用和前面string使用迭代器是一样的

	vector<int> v2(4, 100);
	vector<int>::iterator it = v2.begin();
	while (it != v2.end())
	{
		cout << (*it)*=2<<" ";
		it++;
	}
	cout << endl;

(2)operator[]下标的方式
就是遍历数组一样的道理

vector<int> v2(4, 100);
	for (size_t i = 0; i < v2.size(); i++)
	{
		v2[i]=20;
		cout << v2[i] << " ";
	}
	cout << endl;

(3)范围for
通过前面string的学习,我们知道范围for是基于迭代器的存在去使用的,既然我们有了迭代器的存在,那看看范围for怎么使用

vector<int> v2(4, 100);
	for (auto& i : v2)
	{
		i=50;
		cout << i << " ";
	}
	cout << endl;

我们还是喜欢下标的方式来遍历vector,毕竟用的时候最长。

1.4vector的空间增长问题

在这里插入图片描述
对于接口什么意思我就不做过多的解释了,和string类的意思一样,直接来看使用

vector<int> v2(4, 100);

	cout << "一开始的size:" << v2.size() << endl;
	cout << "一开始的capacity:" << v2.capacity() << endl << endl;

	cout << "进行resize改变有效字符个数"<<endl;
	v2.resize(6);
	cout << "resize(6)后的size:" << v2.size() << endl;
	cout << "resize(6)后的capacity:" << v2.capacity() << endl << endl;

	cout << "进行reserve改变容量" << endl;
	v2.reserve(15);
	cout << "reserve(15)后的size:" << v2.size() << endl;
	cout << "reserve(15)后的capacity:" << v2.capacity() << endl << endl;

	cout <<"empty的测试:" << v2.empty() << endl;

在这里插入图片描述

注意: 我们来看一段代码

vector<int> v1;
	v1.reserve(10);
	for (size_t i = 0; i < 10; i++)
	{
		v1[i] = i;
	}
	for (size_t i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;

在这里插入图片描述
我们一开始创建一个vector对象是没有空间的,我们进行扩容之后,想给将vector里面的数据改成0-9,结果运行却崩溃了,原因是:使用[]来进行访问vector里面的数据会有检查,是通过size的范围来确定的,通过上面的演示,你改变capacity的值,并不会改变size的,但是改变size的值有可能改变capacity的值,所以将reserve换成resize就可以了
在这里插入图片描述

使用迭代器和范围for都不行,他们的终点都是以size为准的

对于扩容每个平台的机制也是不一样的,再vs上市1.5倍扩容,再Linux上是二倍扩容,这个大家下去可以自己测试一下

1.5vector的插入和删除操作

在这里插入图片描述

我们发现有尾插和尾删,也有任意位置的插入和删除,再底层尾插和尾删是复用任意认知的插入和删除,再模拟实现·的时候再给大家介绍吧,我们先来看看这几个怎么使用吧。

对于push_back和pop_back

	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	v1.push_back(5);
	v1.push_back(6);
	for (size_t i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout<<endl;
	v1.pop_back();
	v1.pop_back();
	for (size_t i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}

在这里插入图片描述
对于insert
在这里插入图片描述

  1. 第一种传一个迭代器表示再vector的哪个位置插入一个元素
	vector<int> v1;
	v1.insert(v1.begin(), 1);
	v1.insert(v1.begin(), 2);
	v1.insert(v1.begin(), 3);
	v1.insert(v1.begin(), 4);
	v1.insert(v1.begin(), 5);
	v1.insert(v1.begin(), 6);
	for (size_t i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}

在这里插入图片描述

  1. 第二种传一个迭代器表示再vector的哪个位置插入n个元素
	v1.insert(v1.begin(), 1,100);
	v1.insert(v1.begin(), 2,200);
	v1.insert(v1.begin(), 3,300);
	v1.insert(v1.begin(), 4,400);
	v1.insert(v1.begin(), 5,500);
	v1.insert(v1.begin(), 6,600);
	for (size_t i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}

在这里插入图片描述

  1. 第三种是传其他迭代的中间的元素插入到任意位置
	vector<int> v1(4,100);
	int a1[4] = { 1,2,3,4 };
	v1.insert(v1.begin() + 1, a1 + 1, a1 + 3);
	for (size_t i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}

在这里插入图片描述

把数组的第二和第三个元素插入到vector的第一个元素后面

对于erase
在这里插入图片描述

  1. 第一种是任意位置的元素
vector<int> v1(4, 100);
v1.erase(v1.begin());
  1. 第二种是从first位置开始删除到last位置
vector<int> v1(4, 100);
v1.erase(v1.begin()+1,v1.begin()+3);

这两种非常简单,我就不做运行演示了

1.6vector的排序操作

我们的vector也可以进行排序,它有对应的算法sort函数,对应的头文件是 < algorithm >
在这里插入图片描述
因为sort不是给单独的一个容器进行使用的,所以不能当成vector本身的成员函数,来看看vector怎么进行排序:

升序:

	vector<int> v1;
	v1.push_back(1);
	v1.push_back(3);
	v1.push_back(4);
	v1.push_back(6);
	v1.push_back(5);
	v1.push_back(0);
	for (size_t i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}
	cout << endl;
	sort(v1.begin(), v1.end());
	for (size_t i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}

在这里插入图片描述

降序:
(1)这就要看我们第二个sort函数了,第三个参数是仿函数
我们定义仿函数greater<int> ggreaer具有大于的意思,前面一个比后面一个大,哪不就是降序吗??
在这里插入图片描述

(2)反向迭代器
在这里插入图片描述
通过仿函数和反向迭代器也可以实现升序,大家下来自己测试看看

1.7vector的查找操作(迭代器失效)

我们的查找函数也不是vector特有的函数,查找函数也和sort一样再算法里面,我们来看看文档里面怎么描述的
在这里插入图片描述
在这里插入图片描述

要注意:找的到就返回对应的迭代器,找到就返回此迭代器的下一个位置,迭代器都是左闭右开的,所以找不到是返回的pos=v.end()

代码演示:

	vector<int> v1;
	v1.push_back(1);
	v1.push_back(6);
	v1.push_back(4);
	v1.push_back(5);
	v1.push_back(3);
	v1.push_back(0);
	/// <summary>
	///我们尝试找到元素6然后删除
	vector<int>::iterator pos = find(v1.begin(), v1.end(),6);
	v1.erase(pos)
	for (size_t i = 0; i < v1.size(); i++)
	{
		cout << v1[i] << " ";
	}

在这里插入图片描述

我们来看如果有多个重复的数字呢??

在这里插入图片描述
我们为了增加效率每次就不从头开始找了,我们发现上面这个带啊吗运行后,就会崩溃,为什么会这样呢??原因就是迭代器失效了


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

  1. 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。因为一发生扩容行为就会释放之前的空间,开辟一个新的空间。

有的人说我先把空间开的足够大,只要不扩容不就行了,可以说是可以的,但是危险性太大了,万一再公司程序有天数据变得很大,还是要扩容,那个时候再修改就来不及了

那我们刚才的代码是删除,不存在扩容的情况啊,为什么也会造成迭代器失效的问题呢???erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

解决办法: 在使用前,对迭代器重新赋值即可
细心读者应该发现,我上面提到的那些可能会出现迭代器失效的接口都有一个迭代器接口,这个返回值就是新空间的迭代器,前面的代码这样修改就行了:

auto pos = find(v1.begin(), v1.end(),6);
	while (pos != v1.end())
	{
		v1.erase(pos);
		pos= find(pos+1, v1.end(), 10);
	}

现在和大家说这个问题大家应该可以理解,等博主实现模拟实现的时候,再好好跟大家说说,下一篇我将讲解两个题目,让大家看看vector的好处,再下下篇讲模拟实现

二、总结

相信大家对vector的基本使用都掌握了,后面我将会带大家把上面的函数都模拟实现一遍·,这样大家就能更好的理解底层是怎么处理问题的。我们今天的内容就先讲到这里。
请添加图片描述

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

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

相关文章

【Nacos】Nacos 2.2.4支持pg数据库适配改造

Nacos 2.2.4支持postgresql数据库 本文基源码&#xff1a;扩展插件包 网上资料都有&#xff0c;还是个人爬坑补充异常处理记录&#xff0c;以便后续升级改造有漏洞修复 下载源码 https://github.com/alibaba/nacos/releases 添加依赖 根pom添加pg依赖 <postgresql.vers…

跨平台轻量级RTSP服务模块设计思路及实现探讨

技术背景 为满足内网无纸化/电子教室等内网超低延迟需求&#xff0c;避免让用户配置单独的服务器&#xff0c;我们发布了轻量级RTSP服务模块&#xff0c;轻量级RTSP服务解决的核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务&#xff0c;实现本地的音视频数据&#xff…

(4)【轨迹优化篇】方法一:基于Frenet车道线坐标系,采用解耦采样五次多项式拟合进行局部规划

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言A、第一章&#xff1a;Frenet车道线坐标系介绍及坐标转换一、frenet坐标系介绍1.Frenet坐标的定义2.ST图&#xff08;纵向速度…

【ARM Coresight 系列文章 3 - ARM Coresight 组件 DAP(Debug Access Port) 介绍】

文章目录 1.1 Debug Access Port1.2 Access Port1.2.1 IDR 寄存器 1.3 Mem-APs 介绍1.3.1 Debug 寄存器访问模型1.3.2 APs 中寄存器的介绍 1.1 Debug Access Port 外部 Debugger(DS-5/Trace32) 会通过JTAG接口或者SWD接口和DAP相连&#xff1a; JITAG 一般是5个pin&#xff1…

JAVA每日一练(1)

【程序1】 题目&#xff1a;古典问题&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子对数为多少&#xff1f; import java.util.Scanner;/*…

【ElasticSearch】数据聚合语法与Java实现

文章目录 1、聚合的分类2、DSL实现bucket聚合3、DSL实现Metrics 聚合4、RestClient实现聚合5、需求&#xff1a;返回过滤条件的信息6、带过滤条件的聚合 1、聚合的分类 聚合&#xff08;aggregations&#xff09;可以实现对文档数据的统计、分析、运算。&#xff08;类比MySQL…

如何记牢托福口语考试的关键词?

一般情况下&#xff0c;托福独立口语一类问题是自由回答间题(Free-choice Response)&#xff0c;如&#xff1a;If you could have any job in the world, what would it be? Use details to support your. response;另一类是选择类问题(Paired-choice Response)&#xff0c;如…

BERT论文解读及实现(一)

BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding 1 论文解读 1.1 模型概览 There are two steps in our framework: pre-training and fine-tuning. bert由预训练模型微调模型组成。 ① pre-training, the model is trained on unlabele…

前端Vue入门-day01

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 Vue 快速上手 Vue 概念 创建实例 插值表达式 响应式特性 开发者工具 Vue 指令 v-show v-if …

【Spring Boot】第一个Spring Boot项目:helloworld

第一个Spring Boot项目&#xff1a;helloworld 本节从简单的helloworld程序开始介绍创建Spring Boot项目的方法和流程&#xff0c;以及Spring Boot项目结构&#xff0c;最后介绍项目中非常重要的pom.xml文件。 1.创建Spring Boot项目 有两种方式来构建Spring Boot项目的基础…

【1++的C++初阶】之string

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的C初阶】 文章目录 一&#xff0c;浅谈string类二&#xff0c;string 类常用接口2.1 string的构造2.2 string类对象的容量操作2.3 string类对象的访问及遍历操作2.4 string类对象的修改操作2.…

Python 有趣的模块之pynupt——通过pynput控制鼠标和键盘

Python 有趣的模块之pynupt ——通过pynput控制鼠标和键盘 文章目录 Python 有趣的模块之pynupt ——通过pynput控制鼠标和键盘1️⃣简介2️⃣鼠标控制与移动3️⃣键盘控制与输入4️⃣结语&#x1f4e2; 1️⃣简介 &#x1f680;&#x1f680;&#x1f680;学会控制鼠标和键盘是…

Mongodb连接数据库

1.初始化 npm init 2.安装mongoose npm i mongoose 3.导入mongoose const mongooserequire("mongoose") 4.连接mongodb服务 mongoose.connect("mongodb://127.0.0.1:27017/user") 说明&#xff1a;mongodb是协议,user是数据库&#xff0c;如果没有会自动创…

经OPA运放后,读取电压出错

问题&#xff1a; 在焊接完两块板子上传程序测试时&#xff0c;程序上传完成&#xff0c;有一块板子在使用OPA读取电压时&#xff0c;在未插入电阻情况下&#xff0c;电压读取是正确的&#xff0c;在插入50K电压后&#xff0c;电压值应该是之前的两倍&#xff0c;但是电压变化…

unittest单元测试2

目录 unittest框架解析 构建测试套件 用例的执行顺序 unittest断言 HTML报告生成 异常捕捉与错误截图 数据驱动 &#x1f381;更多干货 完整版文档下载方式&#xff1a; unittest框架解析 unittest 是python 的单元测试框架&#xff0c;unittest 单元测试提供了创建测…

怎么把CAJ转换成PDF文件格式?分享这两个方法!

随着互联网的发展&#xff0c;中国知网(CNKI)已成为许多学术研究人员和学生们获取文献资料的重要来源。在CNKI上&#xff0c;常见的文件格式是CAJ&#xff08;China Academic Journals&#xff09;。然而&#xff0c;由于个人喜好或特定需求&#xff0c;我们有时会希望将这些CA…

PDF文档转化为HTML网页格式怎么操作?分享这三个方法给大家!

PDF文档作为一种常见的文档格式&#xff0c;广泛应用于各个领域。然而&#xff0c;如果您想将PDF文档直接发布到网站上&#xff0c;或是想在网页上进行展示&#xff0c;您可能需要将PDF转化为HTML格式。在此&#xff0c;我为大家介绍三种将PDF转化为HTML格式的方法。 方法一&am…

mysql语句练习题,创建表create ,枚举中文字符集设置,修改(update)

作业&#xff1a; 1.创建表&#xff1a; 创建员工表employee&#xff0c;字段如下&#xff1a; id&#xff08;员工编号&#xff09;&#xff0c;name&#xff08;员工名字&#xff09;&#xff0c;gender&#xff08;员工性别&#xff09;&#xff0c;salary&#xff08;员工薪…

d3dx9_43.dll丢失怎么解决(分享三个解决方法)

d3dx9_43.dll是一个Microsoft DirectX的动态链接库文件&#xff0c;它包含了一系列用于图形、音频和输入的功能和接口。它是DirectX 9的一部分&#xff0c;用于提供游戏和其他图形应用程序所需的图形和声音效果。如果计算机中d3dx9_43.dll丢失&#xff0c;会造成很多游戏无法打…

opencv图片根据规则改变颜色

解析 1. 读入图片 2.通道分离 3.像素值在【100&#xff0c;200】之间&#xff0c;赋值128。大于200赋值255&#xff0c;小于100赋值0。 源码 import cv2 img_raw_path"past/unet-test_result0-0-1-0.png" img_rawcv2.imread(img_raw_path) (r,g,b)cv2.split(img_…