首页 > 其他分享 >快速入门 vector 容器

快速入门 vector 容器

时间:2024-09-19 14:54:35浏览次数:10  
标签:容器 begin cout int 元素 vector 方法 入门

如果一个类存在的意义是为了保存对象,那么就称它为容器(container)。

换句话说,容器是用来保存一堆指定类型对象的集合。

构建程序的重要步骤就是选择一个合适的容器来保存处理任务的数据(该容器本身也能提供一些用于处理数据的有用方法)。

vector 是最有用的容器,它其实就是一个动态的数组,可以根据需要自动调整大小,其中保存的元素在内存中的存储方式是连续的,类似 Java 中的 ArrayList

通常,使用 vector 是最好的选择,除非你有很好的理由选择其他容器。

《C++ Primer》,第五版

vector 的初始化

默认初始化
vector<int> v;

调用默认构造函数,创建一个空的 vector

指定容量的初始化
vector<int> v(10);

创建一个能容纳 10 个元素(这里是 int 类型)的 vector

元素的默认值为类型的初始值,指针的默认值为 nullptr,整数的默认值为 0。

指定容量和初始值的初始化
vector<int> v(10, 100);

创建一个能容纳 10 个 int 类型元素的 vector,其中每个元素的默认值为 100。

列表初始化
vector<int> v = {1, 2, 3, 4, 5};

创建包含指定元素的 vector

拷贝初始化
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2(v1);

通过拷贝构造函数克隆一个旧的 vector 来初始化新的 vector

拷贝方式是深拷贝,即新的 vector 是独立的副本,对新的 vector 中元素的操作并不会反映到旧的 vector 上。

访问 vector 中的元素

下标运算符

使用下标运算符访问指定位置的元素:

vector<int> v = {1, 2, 3, 4, 5};
cout << v[2] << endl; // 输出 3

也可以使用下标运算符来修改某个位置的元素:

vector<int> v = {1, 2, 3, 4, 5};
v[2] = 100;
cout << v[2] << endl; // 输出 100

使用下标运算符去访问元素是不进行索引越界检查的,也就是说可以传入一个明显比实际容量大的索引,而且这样做也不会产生错误,这样读取的值是不确定的。

vector<int> v = {1, 2, 3, 4, 5};
cout << v[2] << endl; // 输出 3
cout << v[200] << endl; // 输出 0

因此用随后介绍的 at() 方法去访问元素会比较安全。

at() 方法
vector<int> v = {1, 2, 3, 4, 5};
cout << v.at(2) << endl; // 输出 3
cout << v.at(200) << endl; // 抛异常

at() 方法会在访问索引越界的时候抛出一个 out_of_range 异常。

常见的索引 for 循环
vector<int> v = {1, 2, 3, 4, 5};
for (int i = 0; i < v.size(); i++) {
    cout << v[i] << endl; // 输出 1 ~ 5
}
范围 for 循环
vector<int> v = {1, 2, 3, 4, 5};
for (int v_: v) {
    cout << v_ << endl; // 输出 1 ~ 5
}

使用范围 for 循环去遍历 vector 中保存的元素。

范围 for 循环的语法比索引 for 循环更简洁,而且还能避免发生越界访问。

可以把 v_ 的类型设置为 auto,让编译器自动推导元素的类型,减少了手动指定类型的麻烦,在类型名很长的情况下特别有用。

for (auto v_ : v) {
    cout << v_ << endl;
}

默认的范围 for 循环是将容器 v 中的每个元素拷贝到变量 v_ 中,由于是值拷贝,所以对 v_ 的修改并不会反映到原本的容器中的元素上。

使用引用访问元素,就可以对原本容器中的元素进行修改:

vector<int> v = {1, 2, 3, 4, 5};
for (auto& v_ : v) {
    v_ += 1; // 把每个数 + 1
}
for (auto v_ : v) {
    cout << v_ << endl; // 输出 2、3、4、5、6
}

这样做还有个好处:避免不必要的拷贝,特别是在遍历处理很大的对象时,可以提升程序性能。

如果只想避免耗时的拷贝,而不想修改原本的元素,加 const

for (const auto& v_ : v) {
    cout << v_ << endl;
}
使用 front() 方法和 back() 方法
vector<int> v = {1, 2, 3, 4, 5};
cout << v.front() << endl; // 输出 1
cout << v.back() << endl; // 输出 5

front() 方法返回 vector 中的第一个元素。

back() 方法返回 vector 中的最后一个元素。

使用 begin() 和 end() 方法

这两个方法返回的是迭代器。

用于和 for 循环配合使用,遍历容器中的元素。

很多函数如 insert()erase() 都需要指定位置,通常 begin()end() 会用来标识某个位置。

标准库算法如 sort()find(),要求传入 begin()end() 迭代器,以指定操作的范围。

begin() 方法返回指向 vector 中第一个元素的迭代器。

end() 方法返回指向 vector 尾部元素之后一个位置的迭代器(比最后一个元素再后一位)。

vector<int> v = {1, 2, 3, 4, 5};
for (auto it = v.begin(); it < v.end(); ++it) {
    cout << *it << endl;  // 输出 1、2、3、4、5
}

begin() 指向的是头元素,所以可以通过 ***** 来获取头元素的值,但是 end() 方法并不指向有效的元素,所以不能通过 ***** 来获取值。

往 vector 中添加元素

push_back() 方法
vector<int> v = {1, 2, 3, 4, 5};
v.push_back(6);
for (int v_ : v) {
    cout << v_ << endl; // 输出 1、2、3、4、5、6
}

vector 的末尾追加一个元素。

insert() 方法

vector 的指定位置插入一个或多个元素。

插入单个元素:

vector<int> v = {1, 2, 3, 4, 5};
v.insert(v.begin() + 2, 10);
for (int v_ : v) {
    cout << v_ << endl;  // 输出 1、2、10、3、4、5
}

该方法接收的参数是迭代器,而不是索引!

插入多个元素:

vector<int> v = {1, 5};
v.insert(v.begin() + 1, {2, 3, 4});
for (int v_ : v) {
    cout << v_ << endl;  // 输出 1、2、3、4、5
}

在指定位置插入新添加的元素之后,会将原位置上的元素及其之后的所有元素全部向后移动,尽量不要在数组中间频繁地插值。

assign() 方法

用法一:先将当前的 vector 清空,并批量插入多个相同的元素。

vector<int> v = {1, 2, 3, 4, 5};
cout << v.size() << endl; // 输出 5
v.assign(3, 100); // 插入 3 个 100
cout << v.size() << endl; // 输出 3
for (int v_ : v) {
    cout << v_ << " "; // 输出 100、100、100、100、100
}

用法二:使用另一个 vector 指定范围内的元素来替换当前 vector 中所有的元素。

vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {6 ,5, 4, 3, 2, 1};

v2.assign(v1.begin() + 1, v1.end() - 1);  // 取 2、3、4 赋值给 v2

for (int v_ : v2) {
    cout << v_ << endl;  // 输出 2、3、4
}

v2 中原有的元素都被清除了,然后才赋值新的元素。

删除 vector 中的元素

pop_back() 方法
vector<int> v = {1, 2, 3, 4, 5};
v.pop_back();
for (int v_ : v) {
    cout << v_ << endl; // 输出 1、2、3、4
}

删除 vector 末尾的元素。

该函数的返回值是 void,被删除的元素不会返回。

clear() 方法
vector<int> v = {1, 2, 3, 4, 5};
v.clear();
cout << v.capacity() << endl;  // 输出 5
cout << v.size() << endl;  // 输出 0

用于清空 vector,删除保存的所有元素,使 vector 的大小变为 0,但并不改变最大容量。

erase() 方法

用于删除指定位置的一个元素或指定范围内的全部元素。

删除单个元素:

vector<int> v = {1, 2, 3, 4, 5};
v.erase(v.begin());  // 删除头元素
for (int v_ : v) {
    cout << v_ << endl;  // 输出 2、3、4、5
}

注意:erase() 方法接受的参数不是索引,也不是值,而是迭代器。

删除指定位置的元素后,后面的元素会往前移动。

删除一个连续范围内的全部元素:

vector<int> v = {1, 2, 3, 4, 5};
v.erase(v.begin(), v.begin() + 3);  // 删除前三个元素
for (int v_ : v) {
    cout << v_ << endl;  // 输出 4、5
}

删除范围时,使用的是半开区间 [first, last),包含头,不包含尾,即删除 firstlast - 1 之间的元素。

调整容量的方法

reserve() 方法
vector<int> v;
v.reserve(5);  // 预先为 5 个元素分配内存空间
cout << v.capacity() << endl;  // 输出 5
cout << v.size() << endl;  // 输出 0

预先分配指定数量的存储空间,避免在添加元素时频繁的重新分配内存。

这只改变 vector 的最大容量,并不添加新的元素。

注意:使用 reserve() 方法之后,相当于新开了一块内存空间,然后把原来的那一堆值全部拷贝过去,原来的那块内存空间将被回收,若此时继续使用指向原来那块内存空间地址的指针,获取的值是随机的,而且还不会报错。

vector<int> v = {1, 2, 3, 4, 5};
int *ptr = &v[0];
cout << *ptr << endl; // 1
v.reserve(10);
cout << *ptr << endl; // 输出不可预知的值
resize() 方法
vector<int> v = {1, 2, 3, 4, 5};
v.resize(3);
cout << v.capacity() << endl;  // 输出 5
cout << v.size() << endl;  // 输出 3
for (int v_ : v) {
    cout << v_ << endl; // 输出 1、2、3
}

调整 vector 的元素数量。如果新的 size 比当前的 size 大,vector 会添加新元素,如果容量不够,则执行扩容的操作,新增的元素将用默认值填充(例如 int 类型的默认值为 0)。

如果想指定新增元素的默认值,则使用:

v.resize(10, 100); // 会将新增的元素填充为 100。

如果新的 size 比当前的 size 小,vector 中的多余元素将会被移除,但最大容量不会发生变化。

shrink_to_fit() 方法

请求缩减 vector 的容量(capacity)以适应当前的大小(size),减少内存的浪费。

容量是容器最多可以存储的元素数量,而大小是实际存储的元素数量。当 vector 扩容时,容量会增加;但移除元素后,容量不会自动减少,这就导致了内存的浪费。

vector<int> v = {1, 2, 3, 4, 5};
cout << v.capacity() << endl;  // 输出 5

v.resize(3);  // 将 size 缩减为 3,但容量保持不变
cout << v.capacity() << endl; // 输出 5

v.shrink_to_fit();  // 请求释放多余的容量
cout << v.capacity() << endl; // 输出 3

注意这只是请求,实际缩不缩还得看编译器心情。

其它有用的方法

empty() 方法
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2;
cout << v1.empty() << endl; // 输出 0
cout << v2.empty() << endl; // 输出 1

检查 vector 是否为空。

size() 方法
vector<int> v = {1, 2, 3, 4, 5};
cout << v.size() << endl; // 输出 5

返回当前 vector 中保存了多少个元素。

capacity() 方法
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2(30);
vector<int> v3;
cout << v1.capacity() << endl; // 输出 5
cout << v2.capacity() << endl; // 输出 30
cout << v3.capacity() << endl; // 输出 0

返回当前 vector 最多可以存储多少个元素。

swap() 方法
vector<int> v1 = {1, 2, 3};
vector<int> v2 = {4, 5, 6};

v1.swap(v2); // 交换 v1 和 v2 的内容

for (int num : v1) {
    cout << num << " "; // 输出 4、5、6
}
cout << endl;

for (int num : v2) {
    cout << num << " "; // 输出 1、2、3
}

交换两个 vector 中保存的元素。

标签:容器,begin,cout,int,元素,vector,方法,入门
From: https://blog.csdn.net/Slade_XiaoZe/article/details/142360085

相关文章

  • [Python]案例驱动最佳入门:Python数据可视化在气候研究中的应用
    在全球气候问题日益受到关注的今天,气温变化成为了科学家、政府、公众讨论的热门话题。然而,全球气温究竟是如何变化的?我们能通过数据洞察到哪些趋势?本文将通过真实模拟的气温数据,结合Python数据分析和可视化技术,带你深入了解全球气温的变化趋势,并揭示气候变化背后的奥秘。1.......
  • STM32F407单片机编程入门(七)USART串口485通讯实战含源码
    文章目录一.概要二.USART串口基本介绍三.STM32单片机USART内部结构图四.USART内部信号流向五.USART示波器信号解析六.485通讯基本概念七.CubeMX配置一个USART数据收发例程进行485通讯实验八.工程源代码下载九.小结一.概要USART(UniversalSynchronous/AsynchronousR......
  • 【大模型专栏—入门篇】CUDA入门与AutoDL“炼丹”
    大模型专栏介绍......
  • 【大模型专栏—入门篇】一文打通你的Pytorch安装
    大模型专栏介绍......
  • 即时通讯框架MobileIMSDK的H5端开发快速入门
    ► 相关链接:① MobileIMSDK-H5端的详细介绍② MobileIMSDK-H5端的开发手册new(* 精编PDF版)一、技术准备您是否已对Web端即时通讯技术有所了解?1)新手入门贴:史上最全Web端即时通讯技术原理详解2)Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE您需要对WebSocket技......
  • 【Gateway 快速入门】
    Gateway快速入门要求:通过浏览器访问api网关,然后通过网关将请求转发到商品微服务基础版第1步:创建一个api-gateway的模块,导入相关依赖<dependencies><!--gateway网关--><dependency><groupId>org.springframework.cloud</groupId>......
  • 图形学系列教程,带你从零开始入门图形学(包含配套代码)—— 透明度和深度
    图形学系列专栏序章初探图形编程第1章你的第一个三角形第2章变换顶点变换视图矩阵&帧速率第3章纹理映射第4章透明度和深度第5章裁剪区域和模板缓冲区第6章场景图第7章场景管理第8章索引缓冲区第9章骨骼动画第10章后处理第11章实时光照(一)第12章实时光照(二)第13章立......
  • 大模型 LLMs 入门指南:小白的学习之路
    前言很明显,这是一个偏学术方向的指南要求,所以我会把整个LLM应用的从数学到编程语言,从框架到常用模型的学习方法,给你捋一个通透。也可能是不爱学习的劝退文。通常要达到熟练的进行LLM相关的学术研究与开发,至少你要准备数学、编码、常用模型的知识,还有LLM相关的知识的准备......
  • 如何用3个月零基础入门网络安全?_网络安全零基础怎么学习
    前言写这篇教程的初衷是很多朋友都想了解如何入门/转行网络安全,实现自己的“黑客梦”。文章的宗旨是:1.指出一些自学的误区2.提供客观可行的学习表3.推荐我认为适合小白学习的资源.大佬绕道哈!一、自学网络安全学习的误区和陷阱1.不要试图先成为一名程序员(以编程为基础......
  • SQL注入漏洞的检测及防御,零基础入门到精通_sql 防注入检测
    SQL注入(SQLInjection)是一种广泛存在于Web应用程序中的严重安全漏洞,它允许攻击者在不得到授权的情况下访问、修改或删除数据库中的数据。这是一种常见的攻击方式,因此数据库开发者、Web开发者和安全专业人员需要了解它,以采取措施来预防和检测SQL注入漏洞。01什么是SQL注入......