C++必修:STL之vector的了解与使用

news2025/1/15 22:38:44

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

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

所属专栏:C++学习
贝蒂的主页:Betty’s blog

1. C/C++中的数组

1.1. C语言中的数组

在 C 语言中,数组是一组相同类型元素的有序集合。与字符串类似,它的大小在编译时就已经确定,不可更改。

//大小为5的整型数组
int arr1[5] = { 1,2,3,4,5 };
//大小为5的浮点型数组
double arr2[5] = {0.0};

1.2. C++中的数组

同样与string类似,C++为了更加方便就引入了一个支持可动态大小数组的序列容器vecotr。其特点如下:

  1. vector是可变大小的序列容器,采用连续存储空间存储元素,可通过下标高效访问。
  2. 与数组不同,vector大小可动态改变,由容器自动处理。
  3. vector本质上用动态分配数组存储元素,插入新元素时可能重新分配空间,即分配新数组并移动全部元素,此操作时间代价高,但不是每次插入都重新分配。
  4. vector会分配额外空间适应增长,不同库策略不同,但重新分配通常是对数增长间隔,使末尾插入元素能在常数时间完成。
  5. dequelistforward_list相比,vector访问元素及末尾添加和删除元素更高效,非末尾的删除和插入操作效率低,且统一的迭代器和引用更好。
//整型数组
vector<int> v1;
//浮点型数组
vector<double> v2;

并且注意每次使用vector都需要包含头文件#include<vector>。并且vector是一个模版类,所以在使用时需要显示实例化

2. vector的接口

接下来我们将介绍一些vector的常见接口,因为很多接口的作用都与string的接口非常类似,所以很多就不在详细说明,大家具体也可以参考vector的使用。

img

2.1. vector的迭代器

同样的vector中也存在迭代器iterator,因为定义在vector类中,所以其需要通过域作用限定符访问——vector<类型>::iterator

下面将介绍的begin()end()rbeign()rend()的使用访问方法与string中的几乎一摸一样,我们直接上实例演示:

void Test1()
{
	vector<int> v = {1,2,3,4,5,6,7,8};
	vector<int>::iterator it = v.begin();
	cout << "顺序遍历:";
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	cout << "逆序遍历:";
	vector<int>::reverse_iterator rit = v.rbegin();
	while (rit != v.rend())
	{
		cout << *rit << " ";
		++rit;
	}
}

img

当然vector也支持const_iterator,用法也类似,这里就不在赘述。

2.2. vector的初始化与销毁

img

img

同样的vector也支持多种构造函数,拷贝构造以及赋值运算符重载。

void Test2()
{
	//1.默认构造函数初始化
	vector<int> v1;
	//2.n个val初始化
	vector<int> v2(3, 2);
	string s("abcd");
	//3.利用迭代器区间初始化
	vector<int> v3(s.begin(), s.end());
	//4.拷贝构造
	vector<int> v4(v3);
	//5.赋值重载
	v2 = v3;
	//6.可变参数列表初始化
	vector<int> v5 = { 1,2,3,4,5 };
	vector<char> v6 = v4;//error 不同类型不能赋值
}

其中需要注意的是可变参数列表初始化,这是在C++11之后支持的新语法,具体讲解我们之后再谈。

2.3. vector的容量操作

函数名称功能
size返回数组的有效长度
capacity返回数组的容量大小
clear清空数组
empty检查是否为空数组,是则返回ture,否则返回false
reserve请求改变数组的容量
resize重新设置有效元素的数量,超过原来有效长度则用c字符填充
2.3.1. 有效长度与容量大小

vector类中,我们同样可以通过size()容器的有效长度;capacity()返回容器的容量大小。

img

void Test3()
{
	vector<int> v = { 1,2,3,4,5 };
	cout << v.size() << endl;
	cout << v.capacity() << endl;
}

img

在初始化时,vecotr中的sizecapacity一般相同。这时我们也可以通过以下程序探究一下其扩容机制:

void TestExpand()
{
	size_t sz;
	vector<int> v;
	sz = v.capacity();
	cout << "making v grow:"<<endl;
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << endl;
		}
	}
}

img

在VS环境下,vector一般是以1.5倍扩容。但是在Linux环境下一般就以2倍扩容。

img

至于clear()empty()两个函数用法就十分简单,这里就不在赘述了。

2.3.2. 有效长度与容量操作

接下来我们来使用一下vector中的resize()reserve()。其实他们的用法与特点也是与string类中的相同,我们直接上手即可

  1. n<sz时,reserve并不会发生任何改变,resize会删除有效字符到指定大小。
  2. sz<n<capcity时,reserve并不会发生任何改变,resize会补充有效字符(默认为0)到指定大小。
  3. n>capacity时,reserve会发生扩容,resize会补充有效字符(默认为0)到指定大小。
void Test4()
{
	vector<int> v1 = { 1,2,3,4,5 };
	cout << "v1的有效长度为:" << v1.size() << endl;
	cout << "v1的容量大小为:" << v1.capacity() << endl;
	v1.reserve(10);
	cout << "v1的有效长度为:" << v1.size() << endl;
	cout << "v1的容量大小为:" << v1.capacity() << endl;
	v1.resize(8, 10);
	for (auto& e : v1)
	{
		cout << e << " ";
	}
}

img

在这里我们需要注意一个经典错误,如下列代码:

void Test5()
{
	vector<int> v;
	v.reserve(10);
	for (int i = 0; i < 10; i++)
	{
		v[i] = i;
	}
	for (auto& e : v)
	{
		cout << e << " ";
	}
}

img

一旦运行就会发生如上错误,这是为什么呢?因为reserve只是改变了容量capacity并没有改变size,而operator[]访问时元素时是禁止访问下标size以后的元素的,一旦访问就会直接报错。

2.4. vector的访问操作

函数名称功能
operator[]返回指定位置的元素,越界则报错
at返回指定位置的元素,越界则抛异常
back返回字符串最后一个元素
front返回字符串第一个元素

这四个函数的用法也与string中的函数用法相同,我们就直接上手示例

void Test6()
{
	vector<int> v = { 1,2,3,4,5 };
	for (int i=0;i<v.size();i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	cout << "front:" << v.front() << endl;
	cout << "back:" << v.back() << endl;
}

img

2.4. vector的修改操作

函数名称功能
push_back在数组后追加元素
insert在指定位置追加元素
assign使用指定数组替换原数组
pop_back删除数组最后一个元素
erase删除数组指定部分区间
swap交换两个数组

我们首先先介绍最简单的四个函数push_back()pop_back()assign()swap()

void Test7()
{
	vector<int> v = { 1,2,3,4,5,6 };
	cout << "back:" << v.back() << endl;
    //尾插
	v.push_back(7);
    //尾删
	cout << "back:" << v.back() << endl;
	v.pop_back();
	cout << "back:" << v.back() << endl;
	vector<int> vv = { 6,5,4,3,2,1 };
    //n个val赋值给原数组
	vv.assign(3, 2);
	for (int i = 0; i < vv.size(); i++)
	{
		cout << vv[i] << " ";
	}
	cout << endl;
	vv.swap(v);
	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	for (int i = 0; i < vv.size(); i++)
	{
		cout << vv[i] << " ";
	}
}

img

然后我们来介绍insert()与·earse()的用法,这两个函数的用法就与string中的有所不同。首先是insert()函数:

img

void Test8()
{
	vector<int> myvector(3, 100);
	vector<int>::iterator it = myvector.begin();
	//1.向指定位置插入一个元素
	it = myvector.insert(it, 200);
	cout << "myvector contains:";
	for (it = myvector.begin(); it < myvector.end(); it++)
		cout << ' ' << *it;
	cout << endl;
	//2.向指定位置插入n个元素
	myvector.insert(it, 2, 300);
	cout << "myvector contains:";
	for (it = myvector.begin(); it < myvector.end(); it++)
		cout << ' ' << *it;
	cout << endl;
	//3.向指定位置插入一段迭代器区间
	it = myvector.begin();
	vector<int> anothervector(2, 400);
	cout << "myvector contains:";
	for (it = myvector.begin(); it < myvector.end(); it++)
		cout << ' ' << *it;
	cout << endl;
	it = myvector.begin();
	myvector.insert(it + 2, anothervector.begin(), anothervector.end());
	//4.向指定位置插入一段迭代器区间
	int myarray[] = { 501,502,503 };
	myvector.insert(myvector.begin(), myarray, myarray + 3);
	cout << "myvector contains:";
	for (it = myvector.begin(); it < myvector.end(); it++)
		cout << ' ' << *it;
	cout << endl;
}

img

接下来我们继续来使用erase()函数

img

void Test9()
{
	//1.删除迭代器所指元素
	vector<int> myvector;
	for (int i = 1; i <= 10; i++) 
		myvector.push_back(i);
	vector<int>::iterator it = myvector.erase(myvector.begin() + 5);
	it = myvector.erase(it);
	//2.删除一段迭代器区间
	it = myvector.erase(myvector.begin(), myvector.begin() + 3);
	cout << "myvector contains:";
	for (int i = 0; i < myvector.size(); ++i)
		cout << ' ' << myvector[i];
	cout << endl;
}

img

虽然看起来vectorinsert()erase()string的没有什么区别,但是仔细观察就可以发现我们每次使用完迭代器之后都会更新,这是为什么呢?

主要还是因为我们每次插入数组都可能发生扩容,而扩容分为就地扩容与异地扩容。如果发生的异地扩容,这时的迭代器就不在指向原来的空间,而就指向一块释放的内存,我们一旦继续访问就会报错,这种现象我们称为迭代器失效。为了避免出现这种情况,所以我们在使用完迭代器之后需要更新。

img

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

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

相关文章

顺序消费rocketMQ(FIFO先进先出)和小技巧 取模运算的周期性特征来将数据分组。

20240801 一、顺序消费MQ&#xff08;FIFO先进先出&#xff09;介绍 二、一个小技巧&#xff0c;对于取模运算&#xff0c;用来在几以前进行随机选取&#xff0c;取模运算的周期性特征来将数据分组&#xff0c;使用场景对于取模会重复问题 一、顺序消费MQ&#xff08;FIFO先进先…

openeuler下载docker

https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/rhel/9/x86_64/stable/ #清华的网址 https://mirrors.aliyun.com/docker-ce/linux/rhel/9/x86_64/stable/ #阿里云的网址 开始配置 vim /etc/yum.repos.d/docker-ce.repo #写仓库&#xff0c;我这里…

【初阶数据结构篇】归并排序和计数排序(总结篇)

文章目录 归并排序和计数排序前言代码位置归并排序计数排序排序性能比较排序算法复杂度及稳定性分析 归并排序和计数排序 前言 本篇以排升序为例 代码位置 gitee 前篇&#xff1a;【初阶数据结构篇】冒泡排序和快速排序 中篇&#xff1a;【初阶数据结构篇】插入、希尔、选择…

【Qt】QDateTimeEdit

在Qt中&#xff0c;QDateEdit是用于选择日期的微调框&#xff0c;QTimeEdit是用于选择小时和分钟的微调框 QDateTimeEdit则是基于QDateEdit和QTimeEdit的组合控件&#xff0c;能够同时显示日期和时间&#xff0c;并允许用户以交互方式编辑日期 常用属性 属性说明dateTime时间…

electron-updater实现electron全量更新和增量更新——渲染进程UI部分

同学们可以私信我加入学习群&#xff01; 正文开始 前言更新功能所有文章汇总一、两个同心球效果实现二、球内进度条、logo、粒子元素实现2.1 球内包含几个元素&#xff1a;2.2 随机粒子生成方法generateRandomPoint2.3 创建多个粒子的方法createParticle 三、gsap创建路径动画…

基于python的百度迁徙迁入、迁出数据分析(六)

书接上回&#xff0c;苏州市我选取了2024年5月1日——5月5日迁入、迁出城市前20名并求了均值&#xff0c;从数据中可以看出苏州市与上海市的关系还是很铁的&#xff0c;都互为对方的迁入、迁出的首选且迁徙比例也接近4分之一&#xff0c;名副其实的老铁了&#xff1b; 迁出城市…

Seurat-SCTransform与harmony整合学习续(亚群分析)

目录 提取细胞亚群 SCTransform-harmony技术路线 ①亚群SCTransform标准化 ②harmony去批次 这里对上一章的内容进行补充&#xff1a; Seurat-SCTransform与harmony整合学习-CSDN博客 提取细胞亚群 rm(list ls()) library(Seurat)#好像先后需要先后加载 library(patchw…

【Jenkins】在linux上通过Jenkins编译gitee项目

因项目需求近期在linux服务器上部署了Jenkins来自动编译gitee上的项目源码&#xff0c;期间踩到了一些坑&#xff0c;花费了不少时间来处理&#xff0c;特此记录。 所需资源下载列表&#xff1a; Jenkins &#xff1a;https://mirrors.tuna.tsinghua.edu.cn/jenkins/war/2.46…

文件系统 --- 重定向,缓冲区

序言 本篇文章的内容和上一篇文章 &#x1f449;点击查看 紧密相连&#xff0c;所以为了更好的理解本篇文章&#xff0c;需要大家将前置知识准备好哦&#x1f607;。  本文主要向大家介绍文件的重定向&#xff0c;以及基于用户级别的缓冲区和基于操作系统级别的缓冲区。原来看…

AI技术和大模型对人才市场的影响

012024 AI技术和大模型 2024年AI技术和大模型呈现出多元化和深入融合的趋势&#xff0c;以下是一些关键的技术方向和特点&#xff1a; 1. 生成式AI 生成式AI&#xff08;Generative AI&#xff09;在2024年继续快速发展&#xff0c;它能够创造全新的内容&#xff0c;而不仅仅…

Redis——有序集合

目录 1. 添加元素 ZADD 2. 查看全部元素 ZRANGE 3. 查看某个元素的分数 ZSCORE 4. 查看元素的排名 ZRANK SortedSet 也叫 ZSet ,即有序集合&#xff0c; 有序集合与集合的区别&#xff1a; 有序集合的每个元素都会关联一个浮点类型的分数&#xff0c;依赖该分数的的大小对…

《Milvus Cloud向量数据库指南》——多模态融合新纪元:音频、视频与文本的无缝转换

在探讨多模态数据处理与应用的广阔领域中,多模态文本、音频、视频数据的融合与交互成为了近年来人工智能研究的热点之一。这一趋势不仅推动了技术的深度发展,也为众多行业带来了前所未有的创新机遇。本文将深入剖析多模态文本-音频与多模态文本-视频RAG(Retrieval-Augmented…

书生大模型基础岛-第三关:LangGPT结构化提示词编写实践

1.来源和任务 来源&#xff1a; https://github.com/InternLM/Tutorial/blob/camp3/docs/L1/Prompt/task.md 任务&#xff1a; 背景问题&#xff1a;近期相关研究发现&#xff0c;LLM在对比浮点数字时表现不佳&#xff0c;经验证&#xff0c;internlm2-chat-1.8b (internlm2-…

C++——list容器以及手动实现

LIST容器 list概述列表容器属性例子 list函数构造函数默认构造函数&#xff1a;带有元素个数和元素初值的构造函数&#xff1a;范围构造函数&#xff1a;拷贝构造函数&#xff1a;移动构造函数&#xff1a;示例 赋值运算符重载拷贝赋值操作符 (1)&#xff1a;移动赋值操作符 (2…

安全通信|数据加密的由来|加密算法简介|中间人攻击与证书认证|身份验证

&#x1f448;️下一篇 计算机网络-专栏&#x1f448;️ 数据加密的由来|加密算法简介|中间人攻击与证书认证 引言 在客户端(client)-服务器(server)模式下&#xff0c;客户端与服务器间通信&#xff0c;如果明文传输数据&#xff0c;在传输过程被劫持&#xff0c;内容直接泄…

MySQL触发器和存储过程

1、触发器 &#xff08;1&#xff09;&#xff1a;建立触发器&#xff0c;订单表中增加订单数量后&#xff0c;商品表商品数量同步减少对应的商品订单出数量,并测试 mysql> create trigger orders_after_insert_trigger-> after insert on orders for each row-> up…

不知道你们有没有我这样的一种状态...总是这样又总是那样...

各位小伙伴们&#xff0c;我是风尚&#xff0c;我不知道你们有没有那么一刻&#xff0c;就是感觉站在人生的十字路口&#xff0c;感觉自己就像是被扔进了一个没有导航的迷宫&#xff0c;四周都是墙&#xff0c;头顶是蓝天白云&#xff0c;却怎么也找不到出口的方向&#xff1f;…

重磅推荐!GBD再度登顶Lancet!| GBD数据库周报(7.17~7.23)

全球疾病负担&#xff08;GBD&#xff09;是迄今为止规模最大、最全面的一项研究&#xff0c;旨在量化不同地区和不同时期的健康损失&#xff0c;从而改善卫生系统并消除差异。 该研究由华盛顿大学健康指标与评估研究所 (IHME) 牵头&#xff0c;是一项真正的全球性研究&#xf…

MySQL--数据库与表的操作

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 数据库的基本操作 # 1、查看数据库show databases;​# 2、创建数据库create database 数据库名称;​# 3、删除数据库drop databse 数据库名称; 数据表…