18. C++STL 4(vector的使用, 空间增长, 迭代器失效详解)

⭐本篇重点:vector容器的使用详解

⭐本篇代码:c++学习/08.vector_test · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)

目录

一. vector的介绍

二. vector的使用 

2.1 vector的定义 *

2.2 vector的迭代器和遍历

a operator[]访问

b vector迭代器的使用访问 *

c C++11 for循环

2.3  vector的容量操作和空间成长问题

a 容量操作:

b 空间增长问题  ***

2.4 vector的增删查改

 三. 使用vector出现的迭代器失效问题 *

3.1 增容导致迭代器失效

 3.2 erase导致迭代器失效


一. vector的介绍

vector的文档如下:vector - C++ Reference (cplusplus.com)

        1 vector就是一个可以动态增长的数组。和数组一样,可以使用[下标]来访问vector中存储的数据。

        2 vector动态分配数组来存储他的元素。每当插入新元素的时候,空间足够就会在数组尾部插入这个数据。如果空间不够,vector会重新分配一个容量更大的数组,然后将全部元素转移到这个新的数组中

        3 vector为了方便管理空间,每一次分配空间的时候都比实际要存储的空间要大一些。不同的系统和编译器增长的倍数有区别,一般都在1.5倍到2倍数

        4 相对其他序列容器(list, deque, forward_list),vector访问数据的效率更高,在末尾插入删除数据效率高。在头部和中部插入删除数据由于要大量挪动数据,效率较低

二. vector的使用 

2.1 vector的定义 *

vector构造函数的列表如下: 标*表示重要

构造函数的声明说明
vector(); *一个简单的无参构造函数
vector(const vector& x); *拷贝构造函数
vector(size_type n, const value_type& val = value_type())构造并初始化为n个val
vector (InputIterator first, InputIterator last);使用迭代器进行构造

赋值运算符

vector& operator=(const vector& x)使用x这个容器去赋值

2.2 vector的迭代器和遍历

        vector有三种遍历方式

a operator[]访问

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;

int main()
{
	//{}内部的数据可以看成为一个vector
	vector<int> arr1 = { 1,5,9,7,5,3,4,6,8,2 };
	vector<int> arr2(arr1);	//使用拷贝构造函数构造vector

	//1. []访问遍历,可以读写数据		arr1.size()可以获取arr1中元素的大小
	for (int i = 0; i < arr1.size(); i++)
	{
		cout << arr1[i] << " ";
	}
	cout << endl;

	//访问arr2
	for (int i = 0; i < arr1.size(); i++)
	{
		cout << arr1[i] << " ";
	}
	cout << endl;

	return 0;
}

运行结果如下 

b vector迭代器的使用访问 *

可以通过begin/end获取vector的迭代器

iterator解释
begin()获取第一个数据位置 iterator/ const_iterator
end()获取最后一个数据位置 iterator/ const_iterator
rbegin()获取最后一个数据位置 reverse_iterator
rend()获取第一个数据位置的 reverse_iterator

 迭代遍历如下

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;

int main()
{
	//{}内部的数据可以看成为一个vector
	vector<int> arr = { 1,5,9,7,5,3,4,6,8,2 };

	//2.迭代器正向访问
	vector<int>::iterator it = arr.begin();
	while (it != arr.end())
	{
		cout << *it << " ";
		it++; //获取下一个位置的迭代器
	}
	cout << endl;
	//迭代器逆向访问
	vector<int>::reverse_iterator rit = arr.rbegin();
	while (rit != arr.rend())
	{
		cout << *rit << " ";
		rit++;
	}
	return 0;
}

运行结果: 

当然:我们还能通过迭代器修改数据

c C++11 for循环

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;

int main()
{
	//{}内部的数据可以看成为一个vector
	vector<int> arr = { 1,5,9,7,5,3,4,6,8,2 };

	//C++ 11 for循环
	for (const auto& e : arr)
		cout << e << " ";

	cout << endl;
	return 0;
}

运行结果 

这种for循环的本质也是通过迭代器实现的!

2.3  vector的容量操作和空间成长问题

a 容量操作:

操作说明
size()获取这个vector的元素大小
capacity()获取这个vectro的最大容量
empty()判断这个vector是否为空

resize(size_type n)

resize(size_type n, value_type& val)

将vector的size更改为 n 

将vector的size更改为 n, 值为 val

reserve(size_type n)将的vector的capacity更改为 n

b 空间增长问题  ***

        vector为了方便管理空间,每次开辟的空间都比实际的容量要多 并且不同的编译器和系统下,容量增长的倍数是不一样的. 一般是1.5倍 到 2倍数

        经过测试,我发现在VS2022中. 容量增长的倍数是1.5倍

        在Linux (cent os) 中容量增长是2倍

测试代码和结果如下:

vector容量增长倍数经过测试在Windows下(VS2022)中是1.5倍

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int capacity = 0;
	vector<int> arr;
	//不断插入数据并计算其容量,观察容量变化
	for (int i = 0; i < 100; i++)
	{
		if (capacity != arr.capacity())
		{
			capacity = arr.capacity();
			cout << capacity << " ";
		}
		arr.push_back(i);
	}
	return 0;
}

运行结果如下:

可以看到容量的增长倍数是1.5倍

 在VS2022查找vector的push_back源码, 可以看到下面这段代码

在Linux测试的结果如下: 

        vector每一次插入数据的时候,如果size < capacity 就会直接在后面插入数据.

        如果size > capacity 那么vector就会重新开辟一份更大的空间,将原来是数据全部拷贝到新的空间中, 再去插入数据 .

 测试代码:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> arr;

	arr.push_back(1);
	printf("%p\n", &arr[0]); //1个数据打印arr[0]的地址


	arr.push_back(1);
	arr.push_back(1);
	arr.push_back(1);
	arr.push_back(1);
	printf("%p\n", &arr[0]); //5个数据打印arr[0]的地址

	return 0;
}

 运行结果如下:可以看到其地址发生了改变

        这说明, vector如果频繁的去push_back就会不断拷贝复制,导致效率降低

        为了解决这种问题:我们使用vector的时候最好提前预估需要使用的容量, 并且使用resize或者reverse去设置好容量

2.4 vector的增删查改

        学习STL容器基本都要掌握容器的增删查改和底层设计

vector常用增删插改如下:

容量操作说明
push_back()在vector尾部插入一个数据
pop_back()在vector尾部删除一个数据
find() 

在vector中查找一个数据,返回迭代器

这个是STL算法提供的, vector内部没有

insert()在输入的pos位置前面插入一个数据
erase()在输入的pos位置删除一个数据
swap()交换两个元素
operator[]像数组一样可以访问和修改指定下标的元素

测试代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;

void print(const vector<int>& arr)
{
	for (const auto& e : arr)
		cout << e << " ";
	cout << endl;
}

int main()
{
	//增删查改
	vector<int> arr;
	arr.push_back(1); //1
	arr.push_back(2); //1 2 
	arr.pop_back();	  //1

	arr.push_back(3); //1 3
	arr.push_back(4); //1 3 4
	print(arr);		//结果应该为: 1 3 4

	auto pos = find(arr.begin(), arr.end(), 3);
	arr.insert(pos, 5);	//在3的前面插入5  1 5 3 4
	pos = find(arr.begin(), arr.end(), 4);
	arr.erase(pos);	//删除pos位置的4
	print(arr);	//结果应该为: 1 5 3

	return 0;
}

测试结果如下:

 三. 使用vector出现的迭代器失效问题 *

3.1 增容导致迭代器失效

 先看一份代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;

int main()
{
	vector<int> arr = { 1,2,3,4,5 };
	auto it = arr.begin();

	arr.push_back(6);
	arr.push_back(7);
	while (it != arr.end())
	{
		cout << *it << " ";
		it++;
	}
	return 0;
}

这份代码看着好像没啥问题,但是在运行之后去报错了!

 

经过调试之后,发现是迭代器非法访问

调试过程如下:

        经过调试知道和vector增容的机制可以知道:

设置好一个迭代器后,vector如果发生了增容. 就会销毁旧空间,开辟新空间

而it仍然指向旧的空间, 这个一来*it就会非法访问.

所以我们使用push_back,insert,reverse,resize这些接口的时候.

要注意调用这些接口之前的迭代器不可使用.

调用接口之后要重新定义迭代器进行使用

 3.2 erase导致迭代器失效

同样先看一份代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;

int main()
{
	//我们想要删除arr中的偶数
	vector<int> arr = { 1,2,3,4,5,6 };
	auto it = arr.begin();

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

	for (const auto& e : arr)
		cout << e << " ";
	cout << endl;
	return 0;
}

我们想要删除偶数,运行的时候却崩溃了

        经过简单分析:在我们删除数据之后,it就失效了

因为删除it之后,it指向的位置就不对了

可以看到报错

一般来说:我们解决迭代器失效的方法是 对迭代器重新赋值

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;

int main()
{
	//我们想要删除arr中的偶数
	vector<int> arr = { 1,2,3,4,5,6 };
	auto it = arr.begin();

	while (it != arr.end())
	{
		if ((*it) % 2 == 0)
		{
			//对迭代器重新赋值,erase删除数据后会返回删除数据后面一个数据的迭代器。
			//重新赋值之后就不会出错了!
			it = arr.erase(it);
		}
		else
		{
			it++; //为奇数,直接it增加
		}
	}

	for (const auto& e : arr)
		cout << e << " ";
	cout << endl;
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NAT拓展

NAT ALG&#xff08;NAT应用级网&#xff09; 为某些应用层协议&#xff0c;因为其报文内容可能携带IP相关信息&#xff0c;而普通NAT转化无法将这些IP转化&#xff0c;从而导致协议无法正常运行 例如FTP&#xff0c;DHCP&#xff0c;RSTP&#xff0c;ICMP&#xff0c;IPSEC…

私有库gitea安装

一 gitea是什么 Gitea是一款自助Git服务&#xff0c;简单来说&#xff0c;就是可以一个私有的github。 搭建很容易。 Gitea依赖于Git。 类似Gitea的还有GitHub、Gitee、GitLab等。 以下是安装步骤。 二 安装sqilite 参考&#xff1a; 在windows上安装sqlite 三 安装git…

从零开始理解JVM:对象的生命周期之对象销毁(垃圾回收)

一、JVM参数 在学垃圾回收器之前&#xff0c;我们先要知道&#xff0c;jvm参数是怎么回事。因为配置各种回收器&#xff0c;必须对应各种参数设置。 标准参数&#xff08;-&#xff09; 所有的JVM实现都必须实现这些参数的功能&#xff0c;而且向后兼容 -help-version 非标准参…

C# 数据类型详解:掌握数据类型及操作为高效编码奠定基础

本文将带你深入了解C#中各种数据类型的特点、用途和最佳实践&#xff0c;让你不仅能熟练运用基本类型&#xff0c;还能掌握如何在实际项目中做出最合适的选择。 目录 C#基本语法 C#数据类型 C#类型转换 C#变量常量 C#基本语法 在学习C#之前我们要先知道C#的基础构建是由哪些…

后端-mybatis的多对多

首先准备两张表学生表和课程表&#xff0c;一个学生可以选多个课程&#xff0c;一门课程也可以被多个学生选择。 再建一个学生表和课程表的中间表&#xff0c;包含学生id和课程id。 我们拿查询所有学生 和他们所选的课程为例&#xff0c;写多对多&#xff08;其实就是一对多&a…

05《存储器层次结构与接口》计算机组成与体系结构 系列课

目录 存储器层次结构概述 层次结构的定义 存储器的排名 存储器接口 处理器与存储器的速度匹配 存储器接口的定义 存储器访问命中率 两种接口 第1种方式&#xff1a;并行 命中率的计算 存储器访问时间 第2种方式&#xff1a;逐级 结语 大家好&#xff0c;欢迎回来。…

软件测试——性能测试工具JMeter

1.JMeter介绍 Apache JMeter是一款纯java编写负载功能测试和性能测试开源工具软件。JMeter小巧轻便且免费&#xff0c;逐渐成为了主流的性能测试工具&#xff0c;是每个测试人员都必须要掌握的工具之一。 环境要求&#xff1a; ​ 需要Java8或者更高的版本。 1.1 JMeter的下…

ARP表、MAC表、路由表的区别和各自作用

文章目录 ARP表、MAC表、路由表的区别和各自作用同一网络内:ARP表request - 请求reply - 响应 MAC地址在同一网络内,交换机如何工作? 不同网络路由表不同网络通信流程PC1到路由器路由器到PC2 简短总结 ARP表、MAC表、路由表的区别和各自作用 同一网络内: ARP作用: 让发送方知…

【Azure Cache for Redis】Redis的导出页面无法配置Storage SAS时通过az cli来完成

问题描述 在Azure Redis的导出页面&#xff0c;突然不能配置Storage Account的SAS作为授权方式。 image.png 那么是否可以通过AZ CLI或者是Powershell来实现SAS的配置呢&#xff1f; 问题解答 可以的。使用 az redis export 可以实现 az redis export --container --prefix[--a…

【AI系统】昇腾 AI 架构介绍

昇腾 AI 架构介绍 昇腾计算的基础软硬件是产业的核⼼&#xff0c;也是 AI 计算能⼒的来源。华为&#xff0c;作为昇腾计算产业⽣态的⼀员&#xff0c;是基础软硬件系统的核⼼贡献者。昇腾计算软硬件包括硬件系统、基础软件和应⽤使能等。 而本书介绍的 AI 系统整体架构&#…

transformers microsoft--table-transformer 表格识别

一、安装包 pip install transformers pip install torch pip install SentencePiecepip install timm pip install accelerate pip install pytesseract pillow pandas pip install tesseract 下载模型&#xff1a; https://huggingface.co/microsoft/table-transformer-s…

给UE5优化一丢丢编辑器性能

背后的原理 先看FActorIterator的定义 /*** Actor iterator* Note that when Playing In Editor, this will find actors only in CurrentWorld*/ class FActorIterator : public TActorIteratorBase<FActorIterator> {//..... }找到基类TActorIteratorBase /*** Temp…

Q3营收同比增22.4%,即时配送高质量增长的美团未来何在?

首先&#xff0c;美团核心本地商业的稳健发展为其未来奠定了坚实的基础。核心本地商业营收达694亿元&#xff0c;同比增长20.2%&#xff0c;这显示出美团在本地生活服务领域的强大竞争力。随着中国经济的高质量发展和消费信心的提升&#xff0c;美团的年交易用户数、年活跃商户…

基于R语言森林生态系统结构、功能与稳定性分析与可视化

在生态学研究中&#xff0c;森林生态系统的结构、功能与稳定性是核心研究内容之一。这些方面不仅关系到森林动态变化和物种多样性&#xff0c;还直接影响森林提供的生态服务功能及其应对环境变化的能力。森林生态系统的结构主要包括物种组成、树种多样性、树木的空间分布与密度…

2024 ccpc 辽宁省赛 E(构造 思维?)L(二分+一点点数论知识?)

E 题意&#xff1a; 可以注意到&#xff1a; 我的两种方格都四个方格的大小。 所以 如果存在一种摆放方式 那么 4|nm。 再考虑一种特殊的情况 22 &#xff0c;此时虽然我的积是4 但是无法摆放的。 1>对于 4 | n,或者 4 | m.我直接摆放第二种方格就可以了。 如果我n 是4 的…

Leetcode 二叉树的锯齿形层序遍历

算法思想&#xff1a; 这段代码实现了 二叉树的锯齿形层序遍历&#xff0c;其核心思想是基于广度优先搜索&#xff08;BFS&#xff09;进行层序遍历&#xff0c;并根据当前层数决定从左到右或从右到左的顺序来组织每一层的节点值。 level.add 和 level.addFirst 有点类似单链…

c++哈希表(原理、实现、开放寻址法)适合新手

c系列哈希的原理及实现&#xff08;上&#xff09; 文章目录 c系列哈希的原理及实现&#xff08;上&#xff09;前言一、哈希的概念二、哈希冲突三、哈希冲突解决3.1、开放寻址法3.2、删除操作3.3、负载因子四、代码实现 总结 前言 红黑树平衡树和哈希有不同的用途。 红黑树、…

用MATLAB符号工具建立机器人的动力学模型

目录 介绍代码功能演示拉格朗日方法回顾求解符号表达式数值求解 介绍 开发机器人过程中经常需要用牛顿-拉格朗日法建立机器人的动力学模型&#xff0c;表示为二阶微分方程组。本文以一个二杆系统为例&#xff0c;介绍如何用MATLAB符号工具得到微分方程表达式&#xff0c;只需要…

MongoDB集群分片安装部署手册

文章目录 一、集群规划1.1 集群安装规划1.2 端口规划1.3 目录创建 二、mongodb安装&#xff08;三台均需要操作&#xff09;2.1 下载、解压2.2 配置环境变量 三、mongodb组件配置3.1 配置config server的副本集3.1.1 config配置文件3.1.2 config server启动3.1.3 初始化config …

java 调用 k8s crd 生成 crd model

k8s官方提供了自动生成Java模型代码的工具&#xff0c;使用指南&#xff1a; https://github.com/kubernetes-client/java提供有两种方法&#xff1a; github action远程生成本地docker镜像生成 本地docker镜像生成很简单&#xff0c;跟着官方指南下载镜像执行命令即可&…