C++ [STL之vector的使用]

news2024/11/27 1:24:22

STL之vector的使用

本文已收录至《C++语言和高级数据结构》专栏!
作者:ARMCSKGT

在这里插入图片描述


STL之vector的使用

  • 前言
  • 正文
    • 默认成员函数
      • 普通构造
      • 拷贝构造
      • 析构函数
      • 赋值重载
    • 迭代器
      • 正向迭代器
      • 反向迭代器
      • const迭代器
    • 容量类
      • 空间容量查询
      • 空间容量操作
        • 扩容操作
        • 元素数量操作
        • 缩容操作
    • 数据访问
      • 下标访问
      • 头尾元素访问
      • 获取原生指针
    • 元素插入删除操作
      • 尾插尾删
      • 任意位置插入删除
      • 任意位置插入
      • 任意位置删除
    • 其他操作函数
      • 交换函数
      • 清空函数
  • 最后


前言

vector是可变大小的数组序列容器,一般也叫向量;底层原理是顺序表,但是vector是泛型容器,可以支持int,double甚至自定义类型的存储,在平时应用非常频繁且广阔,vector在很多场景下可以提高我们的开发效率,所以学习vector这一利器的使用是必须的!
vector


正文

本文将介绍关于vector的常用接口,依据C++ vector官方文档!
首先在使用vector前,需要声明头文件 < vector > 且声明命名空间std!

默认成员函数


vector有三种常规构造,但是在C++11中又增加了一种好用的构造方法!
vector默认成员函数


普通构造

vector支持以下构造:

  • 默认构造(生成一个空容器对象,没有任何数据)
  • 构造n个元素为val的对象
  • 迭代器区间构造
  • 列表初始化构造
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v1; //默认构造

	vector<int> v2(5, 10); //构造5个元素为10的对象

	int arr[] = { 1,2,3,4,5 };
	vector<int> v3(arr, arr + (sizeof(arr) / sizeof(arr[0]))); //迭代器区间构造

	vector<int> v4 = { 6,7,8,9,10 }; //列表初始化构造

	return 0;
}

构造函数


拷贝构造

我们在定义对象时,可以让新对象拷贝一个已存在的对象从而完成初始化!

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

int main()
{
	vector<int> v1(10, 1);

	//两种拷贝构造方式
	vector<int> v2(v1);
	vector<int> v3 = v1;

	return 0;
}

拷贝构造


析构函数

析构函数在vector对象生命周期结束时自动调用,我们使用阶段不需要关心!


赋值重载

vector支持赋值拷贝一个对象(前提是该对象已经存在而并非构造),对与C++11支持通过列表初始化赋值!

赋值重载

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

int main()
{
	vector<int> v1 = {1,2,3};
	vector<int> v2;
	v2 = v1; //赋值空对象

	vector<int> v3 = { 4,5,6 };
	v3 = {10,11,12}; //通过列表赋值已存在数据的对象

	return 0;
}

赋值重载
可以发现当对象中存在数据时,对其赋值会情况原有数据,且也可以通过列表对vector对象进行初始化!


迭代器


  • vector支持两种迭代器正向和反向(C++11),这两种迭代器都有const版本!
  • 迭代器是将指针进行封装,构造成为迭代器对象,自己控制可操作行为!
  • vector的迭代器是随机迭代器,在支持++和- -的同时可以 ± 任意一个整数去往任意一个元素,所以可以使用sort排序!
    迭代器

正向迭代器

正向迭代器的遍历,默认从头begin()到尾end()!
begin指向vector中第一个元素的位置,end指向最后一个元素的下一个位置!
begin和end

在判断结束条件时不能使用 > 和 < 等,因为迭代器封装后不知道是否为连续的空间,判断是否等于end即可!

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

int main()
{
	vector<int> v = { 1,2,3,4,5 };
	vector<int>::iterator it = v.begin(); //定义vector<int>迭代器类型
	//auto it = v.begin(); //觉得麻烦也可以使用auto
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}

正向迭代器


反向迭代器

顾名思义,与正向迭代器相反!
rend指向vector中第一个元素的前一个位置,rbegin指向最后一个元素的位置!
rbegin和rend

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

int main()
{
	vector<int> v = { 1,2,3,4,5 };
	vector<int>::reverse_iterator rit = v.rbegin();
	while (rit != v.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
	return 0;
}

反向迭代器
反向迭代器的++是从后向前走,与正向迭代器的行为相反!


const迭代器

在某些场景下例如:

//vector对象const引用传参,在函数中声明的迭代器是const迭代器
void Print(const vector<int>& v) {} 

//范围for使用const修饰元素时底层调用const迭代器
for (const auto& x : v) {}

平时我们很少会用到const迭代器,不过也可以主动定义!

vector<int>::const_iterator //const正向迭代器
vector<int>::const_reverse_iterator ///const反向迭代器

const迭代器无法修改迭代器指向的元素,但是可以更改迭代器的指向!


容量类


关于容量查询和修改的操作:
容量类


空间容量查询

vector支持以下容量查询操作:

  • 查询元素数量size
  • 查询当前容量capacity
  • 查询当前对象是否为空(不存在任何元素)empty
  • 查询对象可容纳最大元素数(预计,并非准确)max_size
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v = { 1,2,3 };
	cout << "size:" << v.size() << endl;
	cout << "capacity:" << v.capacity() << endl;
	cout << "empty:" << (bool)v.empty() << endl;
	cout << "max_size:" << v.max_size() << endl;
	return 0;
}

容量查询
关于max_size会因为实例类型不同,平台不同等因素发生改变!
max_size


空间容量操作

扩容操作

vector支持使用reserve函数手动扩容,在某些元素数已知的场景下可以提前开辟空间提高性能减少内存碎片!

首先,我们了解一下vector自己的扩容机制:

VS平台(PJ版本)
VS平台
g++平台(SGI版本)
g++平台
通过观察我们发现,VS下扩容是呈1.5倍,g++下是呈2倍扩容;这两种扩容策略各有优略,当数据量比较大时,VS频繁扩容导致性能下降;当数据量中等时,g++扩容冗余过多浪费空间!

针对这种情况,我们可以使用reserve提前扩容,避免这两种情况!

vector<int> v;
v.reverse(n); //提前将容量扩大到n

示例:

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

int main()
{
	vector<int> v;
	v.reserve(100);
	cout << "capacity:" << v.capacity() << endl;
	v.reserve(50); //reserve不支持缩容
	cout << "capacity:" << v.capacity() << endl;
	return 0;
}

reserve

元素数量操作

vector支持使用resize函数自定义元素数量大小,如果我们设置的数量大于当前容量就会触发扩容!

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

int main()
{
	vector<int> v = {1,2,3};
	cout << "size:" << v.size() << endl;
	cout << "capacity:" << v.capacity() << endl;
	
	v.resize(50);
	cout << "size:" << v.size() << endl;
	cout << "capacity:" << v.capacity() << endl;

	v.resize(10);
	cout << "size:" << v.size() << endl;
	cout << "capacity:" << v.capacity() << endl;

	return 0;
}

在这里插入图片描述
很显然,resize改变的是size的大小,无论怎样,也不会缩容!
如果resize(n)中,n大于当前的size,则新增长的空间会使用实例化类型的默认构造去初始化!
resize
resize
为了兼容自定义类型,内置类型也支持像定义对象一样初始化和构造,就是通过int()去构造,就像匿名对象一样,只不过int()相当于0!

int main()
{
	int a(1); //对象初始化方式
	int b = int(2); //匿名对象拷贝构造方式
	cout << a << endl;
	cout << b << endl;
	return 0;
}

内置类型初始化

当然resize也支持指定值进行初始化增长的空间

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

int main()
{
	vector<int> v = {1,2,3};
	v.resize(10,668); //指定初始化新元素空间为668
	return 0;
}

resize(668)
相当于reserve,resize既能扩容,还能完成初始化,两者各有应用场景!

缩容操作

C++11为容器提供缩容操作,缩容函数shrink_to_fit在平时用的非常少,因为对性能的消耗巨大!

vector<int> v(100,1);
v.shrink_to_fit(10); //将capacity缩小为10

其底层原理大概是先开辟指定的缩小空间,然后从头开始截取拷贝数据到新空间,多于的数据会被截断删除!
shrink_to_fit


数据访问


vector是顺序表,其范围方式有很多种,除了通用的迭代器范围,还有以下方式可以访问数据:
访问


下标访问

vector底层是顺序表,那必然支持下标随机访问!
下标访问的方式有两种,在原生通过[ ]访问的基础上,库中还有一个at()也可以通过下标访问,两者的区别在于:

  • 通过 [ ] 访问,如果下标越界则会报错
  • 通过 at() 访问如果下标越界会抛异常
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v = {1,2,3};
	for (int i = 0; i < v.size(); ++i)
	{
		printf("v[%d]=%d,v.at(%d)=%d\n",i, v[i], i, v.at(i));
	}
	return 0;
}

下标访问


头尾元素访问

vector支持取头尾元素的引用或const引用,平时用的比较少,了解即可!
front和back

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

int main()
{
	vector<int> v = { 1,2,3 };
	cout << v.front() << endl;
	cout << v.back() << endl;
	return 0;
}

front和back


获取原生指针

vector支持通过data函数获取存储元素的直接原生空间!
通过这个指针像访问数组一样,使用下标访问或*解引用访问和修改每一个空间的元素!
这个平时用的极少,且非常危险,不建议使用!

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

int main()
{
	vector<int> v = { 1,2,3 };
	int* p = v.data();
	p[1] = 4;
	for (const auto& x : v)
	{
		cout << x << endl;
	}
	return 0;
}

data


元素插入删除操作


vector有许多修改元素的操作函数,但我们只需要了解最常用的一部分即可!
常用插入删除


尾插尾删

  • push_back(val)函数可以将val插入到当前vector容器尾部
  • pop_back()函数可以从当前容器尾部删除一个元素
#include <iostream>
#include <vector>
using namespace std;

void Print(const vector<int>& v)
{
	for (const auto& x : v)
	{
		cout << x << " ";
	}
	cout << endl;
}

int main()
{
	vector<int> v = { 4,5,6 };
	v.pop_back();
	Print(v);
	v.push_back(668);
	Print(v);
	return 0;
}

尾插尾删


任意位置插入删除

任意位置增删

任意位置插入

insert函数有三种形式,支持:

  • 在当前迭代器位置插入一个val元素(相当于在一个元素前插入一个元素)并返回新插入元素的迭代器
    这个返回值至关重要!当我们插入元素后,可能触发扩容,那么原迭代器就会失效(迭代器失效),此时insert会返回新的迭代器指向插入元素,以方便后续操作!
  • 在当前迭代器位置插入n个元素val元素,没有返回值
  • 在当前迭代器位置插入任意一个容器的迭代器区间,没有返回值
    对于这两个函数,在插入后必须手动更新迭代器,否则编译器会报错!
#include <iostream>
#include <vector>
using namespace std;

void Print(const vector<int>& v)
{
	for (const auto& x : v)
	{
		cout << x << " ";
	}
	cout << endl;
}

int main()
{
	vector<int> v = { 4,5,6 };
	v.insert(v.begin(), 1); //头插1
	Print(v);

	v.insert(v.begin(), 2); //头插1
	Print(v);

	v.insert(v.begin() + 2, 2, 668); //在2号元素前处插入2个668
	Print(v);

	int arr[] = { 9,6,3 };
	v.insert(v.begin() + 1, arr, arr + (sizeof(arr) / sizeof(arr[0]))); //在1号元素前插入一个数组
	Print(v);

	return 0;
}

insert

任意位置删除

对于erase任意位置删除有两种形式:

  • 删除当前迭代器指向的元素,并返回该元素后的元素迭代器
  • 删除一个该容器迭代器区间,返回该区间后一个元素的迭代器
    删除操作必然会导致迭代器失效,所以每次删除操作都会更新迭代器!
#include <iostream>
#include <vector>
using namespace std;

void Print(const vector<int>& v)
{
	for (const auto& x : v)
	{
		cout << x << " ";
	}
	cout << endl;
}

int main()
{
	vector<int> v = {1,2,3,4,5,6,7,8,9,10};
	Print(v);
	v.erase(v.begin()); //头删
	Print(v);
	v.erase(v.begin() + 2, v.begin() + 7); //删除2-7号元素
	Print(v);
	return 0;
}

erase
删除对于性能的消耗巨大,因为涉及挪动数据,平时使用也不多!


其他操作函数


交换函数

vector有自带的交换函数,其底层是交换双方所管理的空间!

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

void Print(const vector<int>& v)
{
	for (const auto& x : v)
	{
		cout << x << " ";
	}
	cout << endl;
}

int main()
{
	vector<int> v1 = { 1,2,3,4,5 };
	vector<int> v2 = { 6,7,8,9,10 };
	v1.swap(v2);
	Print(v1);
	Print(v2);
	return 0;
}

swap
当然,vector也支持库中统一的swap函数,但是需要声明算法头文件algorithm才能使用!

#include <iostream>
#include <vector>
#include <algorithm> //算法头文件
using namespace std;
int main()
{
	vector<int> v1 = { 1,2,3,4,5 };
	vector<int> v2 = { 6,7,8,9,10 };
	swap(v1,v2);
	return 0;
}

清空函数

clear函数可以将当前vector对象的元素全部清空,但是不会缩容也不会销毁对象!

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

void Print(const vector<int>& v)
{
	for (const auto& x : v)
	{
		cout << x << " ";
	}
	cout << endl;
}

int main()
{
	vector<int> v = { 1,2,3,4,5 };
	cout << "size:" << v.size() << endl;
	cout << "capacity:" << v.capacity() << endl;
	v.clear();
	cout << "size:" << v.size() << endl;
	cout << "capacity:" << v.capacity() << endl;
	return 0;
}

clear


最后

vector的使用介绍到这里就结束了,vector是一款强大的泛型顺序表,他弥补了数组无法动态扩容的缺点,以及泛型思想使得vector可以实例化成任何类型的顺序表,但只其实现只有一份代码;后面我会为大家揭开vector底层的秘密,为大家介绍vector的底层实现!

本次 <C++ vector使用> 就先介绍到这里啦,希望能够尽可能帮助到大家。

如果文章中有瑕疵,还请各位大佬细心点评和留言,我将立即修补错误,谢谢!

结尾

🌟其他文章阅读推荐🌟
C++ <STL之string的使用> -CSDN博客
C++ <STL之string模拟实现> -CSDN博客
C++ <STL简介> -CSDN博客
C++ <模板> -CSDN博客
C++ <内存管理> -CSDN博客
🌹欢迎读者多多浏览多多支持!🌹

​​

​​


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

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

相关文章

numpy log随机产生非常奇怪的数字(np.log的大坑)

背景 有一批信号数据要送到网络里训练&#xff0c;训练之前为了统一量纲&#xff0c;首先根据方差和均值做了一次标准化&#xff0c;然后求了一次能量&#xff08;20*log10(x)&#xff09;&#xff0c;也就是说送进网络里的其实是一个能量谱&#xff0c;但是训练过程中经常蹦出…

Python的用途与学习计划

python的用途&#xff1a; 1、web开发&#xff1b; 2、网络爬虫&#xff1b; 3、数据科学&#xff1b; 4、自动化运维&#xff1b; 5、数据库编程&#xff1b; 6、网络编程&#xff1b; 7、图形处理、数学处理、文本处理&#xff1b; 8、多媒体应用。 其中&#xff0c…

组合问题-回溯算法

1题目 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4], ] 示例 2&#xff1a; 输入&#xff1a;n …

美国国际留学生超136万,本科、硕士和博士各占多少?

国际教育市场研究机构ICEF Monitor近日公布&#xff0c;2022年美国持有F-1和M-1有效学习签证的国际学生数量共计136.2万人&#xff0c;与2021年相比增长了10.1%。 其中&#xff0c;国际学生来美国读本科学士学位的占37%&#xff0c;读硕士学位的占41%&#xff0c;读博士学位的…

为什么WordPress这么难用?(以及如何让它变得简单点)

WordPress 是世界上最受欢迎的网站构建器&#xff0c;为互联网上超过 43% 的网站提供支持。然而&#xff0c;有些人抱怨说 WordPress 比 Squarespace 和 Wix 等解决方案更难使用。 在本文中&#xff0c;我们将解决为何WordPress这么难用的神话&#xff0c;并分享您可以用来毫无…

用于具有缺失模态的脑肿瘤分割的模态自适应特征交互

文章目录 Modality-Adaptive Feature Interaction for Brain Tumor Segmentation with Missing Modalities摘要本文方法Modality-Adaptive Feature InteractionGraph RepresentationGraph Edge ComputationModality Feature Updating 实验结果 Modality-Adaptive Feature Inter…

OpenCV入门-基于Python

图像入门 1. 创建窗口namedWindow()resizeWindow()destroyAllWindow() 2.显示图像imread()imshow()imwrite()waitKey()flip() 代码演示3.显示视频VideoCapure()对象cap.get()cap.isOpened()cap.read()cap.release() 部分功能代码演示VideoWriter()对象VideoWriter_fourcc()writ…

K8s之Pod生命周期、启动停止钩子

文章目录 一、Pod生命周期流程二、初始化容器-initContainers三、主容器操作-containers1、启动钩子-lifecycle.postStart2、停止钩子-lifecycle.preStop 一、Pod生命周期流程 Pod生命周期整个过程 如下图&#xff1a; 1、在启动任何容器之前&#xff0c;前创建 pause 容器&am…

u-boot移植:详细讲解移植u-boot.2022.10版本到imx6ull开发板

目录 一、u-boot编译环境准备 1.安装交叉编译工具链 2.u-boot源码包下载 3.编译 4.安装依赖库 二、U-Boot中添加自己的开发板 1、添加开发板默认配置文件 2、添加开发板对应的头文件 3、添加开发板对应的板级文件夹 4、修改 arch/arm/mach-imx/mx6/Kconfig 5、其他需…

5分钟使用UNI-APP框架创建你的第一个项目

UNI-APP学习系列 5分钟使用UNI-APP框架创建你的第一个项目 文章目录 UNI-APP学习系列前言uni-app框架创建项目一、HBuilderX可视化方式二、 vue-cli命令行方式 总结 前言 UNI-APP学习系列之5分钟创建自己的第一个uni-app项目。 uni-app框架创建项目 创建方式 一、HBuilderX可…

bash: /opt/ros/kinetic/setup.bash: 没有那个文件或目录

有时候打开终端的时候&#xff0c;会在第一行报错&#xff1a; bash: /opt/ros/kinetic/setup.bash: 没有那个文件或目录 bash: /opt/ros/melodic/setup.bash: 没有那个文件或目录 bash: /opt/ros/neodic/setup.bash: 没有那个文件或目录 凡是类似报错与bash有关的&#xf…

【PCIE703】XCKU060+海思视频处理器HI3531DV200-ARM的高性能综合视频图像处理平台设计资料及调试经验

板卡概述 PCIE703是我司自主研制的一款基于PCIE总线架构的高性能综合视频图像处理平台&#xff0c;该平台采用Xilinx的高性能Kintex UltraScale系列FPGA加上华为海思的高性能视频处理器来实现。 华为海思的HI3531DV200是一款集成了ARM A53四核处理器性能强大的神经网络引擎&am…

架构-软件工程模块-2

系统分析 数据流图可能出案例题&#xff0c;状态转换图了解作用即可 用例图、类图选择题多&#xff0c;暴徒了解即可 #mermaid-svg-lGozbtkYJPEQF1eo {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lGozbtkYJPEQF1e…

加密那些事

一、前言 说到加解密也许有些人会有些陌生&#xff08;仅对于初学者或其他行业的学者&#xff09;&#xff0c;先不说那些让人看到千篇一律的概念&#xff0c;就是如果两个人发信息怎样才能不被第三个人知道呢&#xff1f;是不是脑海里冒出了使用某种暗号&#xff0c;只有相互传…

每一个“大佬级别”的测试工程师,都有着怎样的成长那个路线?3分钟为你揭晓谜底!

目录 引言 1.何为软件测试工程师&#xff1f; 2.软件测试工程师的职责&#xff1f; 3.为什么要做软件测试&#xff1f; 4.软件测试的前途如何&#xff1f; 5.工具和思维谁更重要&#xff1f; 6.测试和开发相差大吗&#xff1f; 7.成为测试工程师的必备条件 8.测试的分…

虚拟机栈概述

一、概述 1.1、虚拟机栈背景 由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的。不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。 优点是跨平台&#xff0c;指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实现同样的功…

网络分析和机器学习

文章目录 网络分析1.Introduction to networks and graph foundations and algorithmsNetwork types (social/bio/comp), Euler/Hamilton, Graphs (matrix/adj)Breadth-first search (shortest paths), Depth-first search (conn. compnts) 2. Emergent global / local network…

PS的基础操作

1、图片放大缩小alt鼠标滚轮 2、按住空格不放&#xff0c;拖拽鼠标可以改变图片的位置 3、英文状态下按字母t&#xff0c;再点击想要查看的文字&#xff0c;就可以在菜单栏上看到所有属性&#xff08;取消选中按esc键) 4、点击它可以画选区来查看间距&#xff1b; w代表宽;h代表…

Jenkins版本升级

Jenkins版本过低的时候&#xff0c;一些插件无法升级&#xff0c;会引发一系列错误&#xff0c;这个时候我们就要升级版本了 一、下载更新包 第一种方式&#xff1a;Jenkins页面下载最新包 第二种官网上下载war包(Jenkins官网) 二、打开服务器搜索jenkins.war路径 1、如果Jenk…

CSDN 的 MD编辑器一些用法

一、点击关键语句跳转指定位置 在CSDN写文章的时候&#xff0c;写的文章过长往往会让读者很难找到自己想看的部分&#xff0c;这时候有个 跳转到指定位置功能 就非常的便利。CSDN在MD编辑器上(富文本编辑器只有一种)就提供了两种跳转到指定位置的方法&#xff1a; 一、目录转跳…