【STL】vector的使用

news2024/11/27 17:49:32

目录

前言

默认成员函数

构造函数

拷贝构造

赋值重载

迭代器

正向迭代器

反向迭代器

容量管理

查看容量和大小

扩容

判空

访问数据

下标访问

边界访问

数据修改

尾插尾删

指定位置插入删除

迭代器失效 

清空

​编辑

交换

查找数据

vector可以代替string吗


前言

讲完string和string的模拟实现,今天讲讲vector的使用。虽然说它叫vector,使用时还是我们平常使用的数组,只不过会自动地调节分配的空间。由于在空间中使用的一块连续的空间,因此支持下标访问,使用起来相当地便利,与我们之前学习的string的区别就在于,string只能存储字符,而vector可以存储任意类型的数据。


默认成员函数

构造函数

在 C++98 中有三种构造函数可以对 vector 进行初始化,分别是:

  • 无参进行构造
  • 放入n个相同数据
  • 根据迭代器区间进行构造

其中的 allocator 是空间配置器,只是用于分配空间,目的为增加申请释放空间的效率。

因为 vector 是一个模板类,因此实例化的时候要声明内置类型。例如 vector 中要存 int 类型的数据便写作 vector<int> ,这样才是其完整的类型名。现在我们就可以试试以不同的构造函数来初始化 vector 了。

[注意]: 使用vector时要包含头文件<vector>!!

int main()
{
	string s("abcd");
	vector<int> v1;
	vector<int> v2(3, 2);
	vector<int> v3(s.begin(), s.end());

	return 0;
}

 

v3 中之所以是 97 98 99 100 是因为模板参数选择的是 int 因此 v3 中存的是字符的 ASCII 码。

也可以这样子定义,本质上是发生了一次拷贝构造,这样使用更加与数据接近更加形象和简便。

int main()
{
	vector<int> v4 = { 1,2,3,4,5 };
	return 0;
}

 

拷贝构造

vector 的拷贝构造便是支持对同类型 vector 的拷贝。

int main()
{
	vector<int> v1({ 1,2,3,4,5 });
	vector<int> v2 = v1;
	return 0;
}

 

 若是模板参数不同的 vector 便无法进行拷贝构造

 

赋值重载

赋值重载用于对已存在的两个 vector 之间进行赋值,就是值之间的拷贝。

         

迭代器

迭代器作为 STL 六大组件,必然是绕不开的话题。库中也准备了多种的迭代器供使用者选择。

正向迭代器

正向迭代器由 begin 开始 end 结束,直接使用起来与 string 的迭代器并无区别。

借助迭代器我们既可以直接使用迭代器,也可以使用范围 for 完成对 vector 的遍历了。

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6,7,8,9 };
	vector<int>::iterator it = v1.begin();
    //auto it = v1.begin();     也可以直接使用auto
	while (it != v1.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	for (auto it : v1)
	{
		cout << it << " ";
	}
	cout << endl;
	return 0;
}

  

反向迭代器

反向的迭代器名称为 reverse_iterator,其接口为 rbegin 和 rend ,使用起来与正向迭代器类似。

但值得注意的一点是:范围 for 只能进行正向的遍历,无法反向

 

int main()
{
	vector<int> v1 = { 1,2,3,4,5,6,7,8,9 };
	vector<int>::reverse_iterator it = v1.rbegin();
	//auto it = v1.rbegin();
	while (it != v1.rend())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	return 0;
}

 

容量管理

查看容量和大小

跟 string 一样,我们可以通过 size 和 capacity 两个接口分别访问 vector 的和容量,通过这两个接口也有效帮助我们确定访问时的边界值。

扩容

 通过 reserve 我们能够对 capacity 进行修改,根据传入值的大小会有两种处理方式。

  • n 大于 capacity 时会重新分配一块更大的空间给 vector ,将 capacity 增长到 n 或更大。
  • 其他情况下便不进行处理

同时,reserve 不会更改 vector 的大小和其中的元素。

resize 用于调整 vector 的大小,使其包含 n 个元素,能够对其中的值进行初始化,若未传参数则缺省为 0。

  • 如果n小于当前size,则内容将减少到其前n个元素,删除超出的元素,不更改capacity
  • 如果n大于当前size小于capacity,则通过在末尾插入所需数量的元素来扩展内容,以达到n的大小
  • 如果n大于当前capacity,将自动重新分配存储空间,再将元素填充至n个

使用这段代码便可以直接地观察到上述三种不同的情况。

int main()
{
	vector<int> v = { 1,2,3,4,5 };
	cout << v.size() << " " << v.capacity() << endl;
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	v.resize(20);
	cout << v.size() << " " << v.capacity() << endl;
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	v.resize(10);
	cout << v.size() << " " << v.capacity() << endl;
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	return 0;
}

 

判空

通过 empty 这个接口可以得知当前这个 vector 是否为空,换言之就是 size 是否等于 0。

 

访问数据

下标访问

库中对 [ ] 运算符进行了重载,因此可以使用下标对 vector 的元素直接进行访问。

 结合前面的 size 接口便可以简单实现下标遍历 vector。

边界访问

虽然说用下标直接访问就十分的方便了,但库中还有两个接口用于边界值的访问。

通过这两个接口可以直接访问 vector 中的第一个与最后一个元素。

 

数据修改

讲完数据访问,之后便是对 vector 之中的元素进行修改的操作了。

尾插尾删

库中有两个接口分别是 push_back 和 pop_back 分别对应尾插和尾删的功能。

 可以使用如下代码看看接口的效果。

int main()
{
	vector<int> v = { 1,2,3,4,5 };  //原数组
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	v.pop_back();         //尾删
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	v.push_back(8);       //尾插
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	return 0;
}

 

指定位置插入删除

 insert 支持让我们在指定的位置插入元素,同时有三种插入方式供使用者选择。

  • 指定位置插入一个val
  • 指定位置插入n个val
  • 指定位置插入迭代器区间

可以通过下面的实例代码看看实际使用的效果。

int main()
{
	vector<int> v = { 1,2,3,4,5 };  //原数组
	vector<int> v1 = { 8,9,10 };
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	v.insert(v.begin() + 3, 6);   //插入单个值
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	v.insert(v.begin() + 4, 3, 7);  //插入n个值
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	v.insert(v.begin() + 6, v1.begin(), v1.end());   //插入一段区间
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	return 0;
}

 ​​​​​​​

之后我们可以使用 erase 删除任意位置的元素。可以给定一个迭代器位置,或是一个迭代器区间,删除该区间内的元素。

值得注意的时,传入的迭代器区间其实是左闭右开的,因此迭代器 last 指向的那个元素不会被删除。

int main()
{
	vector<int> v = { 1,2,3,4,5 };  //原数组
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	v.erase(v.begin());   //删除第一个元素
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	v.erase(v.begin() + 1, v.begin() + 3);  //删除[3,5)
	for (auto it : v)
	{
		cout << it << " ";
	}
	cout << endl;
	return 0;
}

 

迭代器失效 

若我们尝试连续插入多个数据的时候,可能会出现这样子的错误。

   

这是由于当我们不断插入数据的时候,如果出现容量不足的情况就会进行扩容,而扩容大概率不会直接在原地扩容。因此在异地开辟了一块新空间,并将原来的元素拷贝过去,从而达到扩容的效果。

此时,我们原先拿到的迭代器中存的仍是原来空间的地址,而原来那块空间已被释放,因此该迭代器中的地址便成了野指针

换言之,这个迭代器就失效了。那我们如何解决这个问题呢?其实 insert 是有一个返回值的,只不过我们之前忽略了它。

即,会返回指向插入的第一个元素的迭代器,因此我们只需要每次将 insert 的返回值再传回给我们的迭代器,就能够避免迭代器失效的问题。

int main()
{
	vector<int> v = { 1,2,3 };
	auto it = v.begin() + 2;
	for (int i = 0; i <= 20; i++)
	{
		it = v.insert(it, i);
	}
	return 0;
}

同样的现象也会出现在 erase 上,其返回的迭代器是指向删除区间的下一个位置。

清空

我们还可以使用 clear 清空 vector 中的所有元素,本质上就是将 vector 的 size 大小改成 0 即可。

交换

之前在 string 我们也讲过,容器之间的交换并不需要完全的拷贝,而是直接交换类中的指针即可。

因此 vector 中的这个交换函数,自然比原生的交换函数效率要来得高

 

 

查找数据

虽然 vector 的库中并没有查找的这个函数,但是在算法库中为我们准备了一个 find 函数可以进行查找。

传入一个迭代器区间,再输入要查找的值即可,找到了则返回指向该元素的迭代器,反之则返回该容器的 end。

int main()
{
	vector<int> v1 = { 1,2,3,4,5 };
	cout << *find(v1.begin(), v1.end(), 4);
	return 0;
}

这个函数通过使用迭代器因而同时支持了多种容器,使用起来相当方便。

vector<char>可以代替string吗

vector 是一种泛型编程,同时支持 int 和 double 等不同类型的存储,而这些存储形式一般用不到 string 之中的某些接口,比如 += 、逻辑运算等。

可以这么说,string 这个容器是专门针对存储字符类型的,因此其种的接口也是专门适用于处理字符串的,而 vector 需要考虑的是如何支持各种类型都能够存储其中而舍弃了 string 之中的部分操作。


好了,今天使用 vector 的讲解到这里就结束了,如果这篇文章对你有用的话还请留下你的三连加关注

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

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

相关文章

VOACAP 软件的简单介绍

VOACAP 软件可以预测短波通信中的最高可用频率、最佳传输频率、角度、延迟、反射点高度、信噪比、收发增益等参数&#xff0c;它可以直接输出文本文件&#xff0c;或者以图表输出&#xff0c;同时&#xff0c;它也可以绘制某一参数随时间、距离的变化图表。 该软件的下载安装可…

C语言从入门到精通第18天(指针和函数的联用)

指针和函数的联用 一级指针作为函数的形参二级指针 一级指针作为函数的形参 当函数的形参为数组时&#xff0c;我们定义函数如下&#xff1a; 语法: 数据类型 函数名(数据类型 数组名) 例如 : void func(int a[],int a){ 语句; } 但是在实际使用中我们通常用指针的形式进行…

GEE:如何进行对MOD09GA数据集进行水体/云掩膜并计算NDVI将其导出至云盘?

目录 01 为什么用GEE而不是传统的下载ENVIArcGIS&#xff1f; 02 操作详解 01 为什么用GEE而不是传统的下载ENVIArcGIS&#xff1f; 由于地理空间数据云中缺少2015年10月份的NDVI月合成影像&#xff0c;于是查看了地理空间数据云的NDVI数据集处理的一些介绍如下(地理空间数据…

【Linux内核】信号量semaphore机制

信号量实现方法 信号量机制是一种用于控制并发访问的同步机制&#xff0c;常用于多进程或多线程之间的协调。在Linux内核中&#xff0c;信号量机制是通过struct semaphore结构体来实现的。 每个semaphore结构体包含一个计数器和一个等待队列&#xff0c;它们用于跟踪当前可用…

Linux 并发与竞争

一、并发与竞争 1、并发 Linux 系统是个多任务操作系统&#xff0c;会存在多个任务同时访问同一片内存区域&#xff0c;这些任务可 能会相互覆盖这段内存中的数据&#xff0c;造成内存数据混乱。 多线程并发访问&#xff0c; Linux 是多任务(线程)的系统&#xff0c;所以多线…

命令firewalld和firewall-cmd用法

firewalld命令跟firewall-cmd 1.启动firewalld服务 systemctl start firewalld.service2.关闭firewalld服务 systemctl stop firewalld.service3.重启firewalld服务 systemctl restart firewalld.service4.查看firewalld状态 systemctl status firewalld.service5.开机自启…

接口测试怎么做?全网最详细从接口测试到接口自动化详解,看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 抛出一个问题&…

孙鑫VC++第三章 2.基于MFC的程序框架剖析

目录 1. MFC向导生成类 2. 框架流程 2.1 WinMain 2.2 全局对象&#xff1a;theApp 2.3 AfxWinMain函数 1.AfxWinMain&#xff1a; 2.AfxGetThread函数&#xff08;thrdcore.cpp&#xff09;&#xff1a; 3.AfxGetApp是一个全局函数&#xff0c;定义于&#xff08;afxwin1…

原型/原型链/构造函数/类

认识构造函数 为什么有构造函数 因为一般的创建对象的方式一次只能创建一个对象, 利用工厂模式创建的对象&#xff0c;对象的类型都是Object类型 什么是构造函数 构造函数也称之为构造器&#xff08;constructor&#xff09;&#xff0c;通常是我们在创建对象时会调用的函数…

Uni-app项目应用总结(一)

目录 一.新建uniapp项目 第一步&#xff1a;下载HBuilder 第二步:创建uni-app项目 第三步&#xff1a;运行uni-app 二.uni-app组件使用 三.uni-app路由跳转  1.页面路由配置    (1)在pages.json中配置页面路由    (2)在pages.json中配置底部导航栏 2.路由跳转方法…

【输配电路 DZY-104端子排中间继电器 接通、信号转换 JOSEF约瑟】

DZY-104端子排中间继电器品牌:JOSEF约瑟型号:DZY-104名称:端子排式中间继电器触点容量:5A/250V功率消耗:≤1.5W/≤3W/≤7W/≤3VA/≤7VA/≥5W绝缘电阻:≥10MΩ 系列型号&#xff1a; DZY-101端子排中间继电器&#xff1b; DZY-104端子排中间继电器&#xff1b; DZY-105端子排…

华南农业大学|图像处理与分析技术综合测试|题目解答:求芒果单层坏损率

设计任务 对于一幅芒果果实内部的 CT 断层图像&#xff0c;试采用图像处理与分析技术&#xff0c;设计适当的算法和程序&#xff0c;首先分割出其中的坏损区域&#xff0c;然后计算其像素面积占整个果肉区域的百分比&#xff08;单层坏损率&#xff09;。请按统一要求写出算法…

nuc980 uboot 2017.11 移植:env 保存位置选择问题

开发环境 Win10 64位 ubuntu 20.04 虚拟机 VMware Workstation 16 Pro 开发板&#xff1a;NK-980IOT&#xff08;NUC980DK61Y&#xff09; gcc 交叉编译工具链&#xff1a; ARM 官方 gcc version 11.2.1 20220111 NUC980 uboot 版本 &#xff1a;尝试移植到 u-boot-2017.1…

科普 “平均工资又涨了”

周四晚上做了一个图&#xff0c;发了一则朋友圈&#xff0c;科普了一下为什么平均工资一直在涨&#xff1a; 曲线是 drawio 画的&#xff0c;不是类似 geogebra 画的精确数学函数&#xff0c;误差比较大&#xff0c;但大概就是这个意思。 收入应该是无标度分形的幂律分布&am…

孙鑫VC++第三章 4.窗口类、窗口类对象与窗口三者之间关系

目录 1. 创建CWnd 2. WinMain 3. 创建CButton 1. 创建CWnd 模拟CWnd类的封装过程。在解决方案ch04下添加一个新的空项目&#xff0c;项目名称为&#xff1a;WinMain&#xff0c;在项目创建完成后&#xff0c;将WinMain项目设为启动项目。 接下来在WinMain项目中添加一个名…

【C++ 学习 ④】- 类和对象(下)

目录 一、初始化列表 1.1 - 定义 1.2 - 使用初始化列表的原因 1.3 - 成员变量的初始化顺序 二、静态成员 2.1 - 静态成员变量 2.2 - 静态成员函数 三、友元 3.1 - 友元函数 3.2 - 友元类 四、内部类 五、匿名对象 5.1 - 匿名对象的特性 5.2 - 匿名对象的使用场景…

3.View的绘制流程

View是在什么时候显示在屏幕上面的?(如:MainActivity的布局文件activity_main.xml) setContentView最终的结果是将解析的xml文件中的View添加到DecorView中. 那么这个DecorView是什么时候添加到Window(PhoneWindow)的呢? DecorView是在ActivityThread.java的handleResumeA…

2-Zookeeper单机版安装

2-Zookeeper单机版安装 本文介绍的是 Linux 系统下 Zookeeper 安装方式 ① 下载 进入官网 https://zookeeper.apache.org/ 点击下载按钮 进入下载页 https://zookeeper.apache.org/releases.html 后选择 最新的稳定版本&#xff0c;如下&#xff1a; 3.7.1 为最新的稳定版本…

号称分割一切的图片分割模型开源了——Segment Anything Meta SAM

头条号:人工智能研究所 微信号:启示AI科技 微信小程序:AI人工智能工具 以前,要解决任何类型的分割问题,有两类方法。第一种是交互式分割,允许分割任何类别的对象,但需要人通过迭代细化掩码来指导。第二种,自动分割,允许分割提前定义的特定对象类别(例如,猫或椅子),…