【STL专题】深入探索vector:动态数组的魔力【入门指南】

欢迎来到 CILMY23 的博客

🏆本篇主题为:深入探索vector:动态数组的魔力,入门指南

🏆个人主页:CILMY23-CSDN博客

🏆系列专栏:Python | C++ | C语言 | 数据结构与算法 | 贪心算法 | Linux | 算法专题 | 代码训练营

🏆感谢观看,支持的可以给个一键三连,点赞收藏+评论。如果你觉得有帮助,还可以点点关注


前言

在模拟实现完string后,才明白大家为什么都在吐槽string,甚至阅读了一篇大佬的发言

STL 的string类怎么啦?_string类在stl里面吗-CSDN博客https://blog.csdn.net/haoel/article/details/1491219

string 的接口繁多,初次学习的时候眼花缭乱,不禁感叹的是,我也写下了最长的一篇博客(链接)。记得写了好几天。

甚至还阅读了一篇好文

C++面试中string类的一种正确写法 | 酷 壳 - CoolShellhttps://coolshell.cn/articles/10478.html这几篇对加深string都有认识,那接下来我们将接触vector容器,作为经典的容器之一,它又会带给我们什么呢?我们接着往下看。

提示:本篇附赠了没有学过string想直接上手的入门指南


vector

一、vector介绍

二、vector的详解

💫vector的接口

💫vector原型

[💫vector 的默认成员函数](#💫vector 的默认成员函数)

🍃构造函数(⭐)

🍃析构函数

🍃赋值运算符重载

[💫vector 的遍历](#💫vector 的遍历)

[💫vector 的扩容机制](#💫vector 的扩容机制)

[💫vector 的对象操作](#💫vector 的对象操作)

[💫vector 中的vector](#💫vector 中的vector)

[💫vector 中的对象数组](#💫vector 中的对象数组)

三、入门指南

💦vector对象创建

💦vector的插入

🍀尾插

🍀任意位置插入

💦vector的常用接口

💦vector的删除

🍀指定位置删除

🍀尾删

💦vector的resize()和reserve()

🍀resize()

🍀reserve()


vector

一、vector介绍

vector文档:

cplusplus.com/reference/vector/vector/https://cplusplus.com/reference/vector/vector/文档当中的介绍我就不像string一样放截图翻译了,说的几点我直接翻译。

  1. 1.vector是一个顺序(序列)容器,它的表现形式是像可以改变大小(长度)数组一样。文档的第一句话就告诉我们vector是表示可变大小数组的序列容器。
  2. 文档的第二段话:就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理
  3. 文档的第三段在说vector的本质,它是利用动态数组实现的:本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是 一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
  4. **这一段是再说vector的空间是如何分配的:**相对的,vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  5. .因此,和数组相比,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
  6. 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末 尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list 统一的迭代器和引用更好。

二、vector的详解

💫vector的接口

vector的接口比起string就没有那么多了,大致接口如下,我们可以看到我们的老伙伴了,构造函数,析构函数,size,还有[]。接下来我们从成员函数开始,一步步学习如何使用吧。

💫vector原型

vector原型如下,第一个参数,class T是一个模板,而第二个是空间配置器,也就是是内存池,所有容器都用内存池来开空间,这样可以提高效率。我们只需要知道,它是用来分配内存即可。

💫vector 的默认成员函数

🍃构造函数(⭐)

cplusplus.com/reference/vector/vector/vector/

进入构造函数的文档界面,我们看到有四个构造函数,其中重点掌握的,也是最常用的,我在图片中已经标出来了。剩下的了解就差不多了。

第一个是默认的构造函数,是无参的,那括号中的又是什么呢?

const allocator_type& alloc = allocator_type()

这个是我们刚刚提到的内存分配,所以不用太在意这一块,之后我们总会接触到的。

第一个无参构造函数,也是我们很经常用的,除此之外还可以使用第四个拷贝构造函数。

使用如下,这一块和string是差不多的。

🍃析构函数

对析构函数我们不必关注太多,稍微看看就行,在学习的时候,我们知道析构函数的功能即可 .

🍃赋值运算符重载

赋值运算符重载,就给了一种形式,

其实际操作如下:

这个应用起来,可比string的文档少的多。

💫vector 的遍历

vector 的遍历和string 是一样的,大家都可以用迭代器,下标加[],以及范围for三种形式。

实际操作:

for (size_t i = 0; i < v3.size(); i++)
{
	cout << v3[i] << " ";
}
cout << endl;

vector<int>::iterator it = v3.begin();
while (it != v3.end())
{
	cout << *it << " ";
	++it;
}
cout << endl;

for (auto e : v3)
{
	cout << e << " ";
}
cout << endl;

当然因为我们还没有插入任何数据,所以是看不到屏幕上的情况的。 我们可以对v1进行push_back一些数据。

然后就可以显示了。

💫vector 的扩容机制

听说VS下的vector扩容和g++的不一样,我们来试试就知道了。

cpp 复制代码
// 测试vector的默认扩容机制
void TestVectorExpand()
{
	size_t sz;
	vector<int> v;
	sz = v.capacity();
	cout << "making v grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

这是VS下的测试:

我们可以发现

在我们的虚拟机上就可以测试出来,代码和上述是差不多的,我就不放出来了,那这里输入的代码如图所示,我们可以看到屏幕给我们展示了vector的2倍扩容。

capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。 这个问题经常会出现,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。

💫vector 的对象操作

是否大家记得在我们写string的时候,提到过find函数,如图所示,

但是vector的介绍这里面没有find,它居然写在算法里面了。

这是算法库的文档

- C++ Reference (cplusplus.com)https://legacy.cplusplus.com/reference/algorithm/?kw=algorithm 我们可以找到这个find函数。

我们还可以通过文档查看value,这段话是说,如果找到了,返回下标,没找到就返回last。这个last 实际上就是我们给的的区间末尾。

所以具体的使用如下:

为什么vector用算法里面的find,而string要用自己的find?

原因可能有以下三点:

1.string写的早

2.string还要支持字串的查找

3.vector只需要找某个值

💫vector 中的vector

以前我们在学习C语言的时候,接触过一个二维数组,我们说二维数组可以看过由一维数组构成的。

我们可以通过一串代码来理解一下vector中的二维表现形式。

cpp 复制代码
void test1()
{
	vector<vector<int>> vv;

	// 初始化
	vv = { {1, 2, 3},
		  {4, 5, 6},
		  {7, 8, 9} };

	// 访问二维矩阵的元素
	cout << "vv[0][1]: " << vv[0][1] << endl; 
	cout << "vv[2][2]: " << vv[2][2] << endl;  
}

解析:

当我们声明 vector<vector<int>> vv; 时,我们创建了一个 vector 对象 vv ,其中每个元素也是一个 **vector<int>**对象。这种嵌套的结构可以被看作是二维数组。

在这个二维数组中,每个元素 vv[i] 也是一个 vector<int> 对象,表示二维数组的一维数组。

我们可以使用下标来访问和操作这个二维数组。

例如,可以使用 vv[i][j] 来访问二维数组中的特定元素,其中 i 表示行下标,j 表示列下标。

就是按二维数组的方式去理解即可,但是又不一样,这个毕竟中间不一定只存vector。结构体中嵌套一个结构体,实际上用指针模拟实现二维数组的时候也可以这么看。

画图理解:

这里因为是整形类型,所以我写的是int* 的指针,当然实际情况可能并不这样,我们只是大概的去类比一下,稍微画画,理解这个二维vector这个概念。

💫vector 中的对象数组

像上一个小节中的,二维vector其实是一个对象数组的概念。

"vector的对象数组"指的是将多个对象存储在一个 vector 容器中,形成一个对象数组的结构。

在C++中,vector 是一个动态数组容器,可以存储多个对象,并且可以根据需要动态调整大小。每个对象可以是相同类型的,也可以是不同类型的。

当我们将多个对象存储在 vector 中时,就形成了一个对象数组。这个对象数组可以根据需要进行扩展或缩小,并且可以使用下标来访问和操作其中的对象。

三、入门指南

记得在使用 vector 之前包含 <vector> 头文件。

💦vector对象创建

vector<type> name

其中,type 是你要存储在 vector 中的对象的类型,而 name 是你给这个 vector 对象起的名称。例如,如果你想创建一个存储整数的 vector 对象,你可以这样写:

cpp 复制代码
vector<int> v

同样,如果你想创建一个存储字符串的 vector 对象,你可以这样写:

cpp 复制代码
vector<string> names

这将创建一个名为 names 的 vector 对象,用于存储字符串类型的数据。

你可以根据需要在程序中创建多个 vector 对象,并使用它们来存储和操作不同类型的数据。

💦vector的插入

🍀尾插

要向 vector 中进行尾插操作,可以使用 push_back() 函数。这个函数将一个元素添加到 vector 的末尾。

cpp 复制代码
vector<int> numbers;

// 进行尾插操作
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);

运行这段代码,输出将会是 1 2 3。

🍀任意位置插入

要向 vector 中任意插入元素,可以使用 insert() 函数。

insert() 函数有多种用法,可以在指定位置插入一个元素,也可以在指定位置插入一个范围内的元素。

在指定位置插入一个元素:

cpp 复制代码
vector<int> v = {1, 2, 3, 4, 5};

// 在位置为 index 的位置插入元素 6
v.insert(v.begin() + index, 6);

在指定位置插入一个范围内的元素:

cpp 复制代码
vector<int> v1 = {1, 2, 3};
vector<int> v2 = {4, 5};

// 在位置为 index 的位置插入 v2 中的元素
v1.insert(v1.begin() + index, v2.begin(), v2.end());

💦vector的常用接口

在 C++ 中,vector 提供了一些常用接口,一起来看看吧。

cpp 复制代码
vector<int> v;

v.size();//返回容器中元素个数
v.begin()//返回头部迭代器
v.end()//返回尾部+1迭代器
v.empty()//判断是否为空
  • v.size():返回 vector<int> 容器中的元素个数。它返回一个无符号整数类型的值,表示容器中元素的数量。
  • v.begin():返回一个指向 vector<int> 容器中第一个元素的迭代器。它指向容器的起始位置。
  • v.end():返回一个指向 vector<int> 容器末尾元素的下一个位置的迭代器。它指向容器的结束位置。
  • v.empty():检查 vector<int> 容器是否为空。如果容器为空,则返回 true;否则返回 false。

💦vector的删除

要从 vector 中删除元素,可以使用 erase() 或 pop_back() 函数。

🍀指定位置删除

使用 erase() 函数删除指定位置的元素:

cpp 复制代码
vector<int> v = {1, 2, 3, 4, 5}; //这是C++11的写法

// 删除位置为 index 的元素
v.erase(v.begin() + index);

🍀尾删

cpp 复制代码
vector<int> v = {1, 2, 3, 4, 5};

// 删除最后一个元素
v.pop_back();

💦vector的resize()和reserve()

resize() 和 reserve() 是 vector 类提供的两个函数,用于调整容器的大小和预分配内存空间。

🍀resize()

resize() 函数用于调整 vector 的大小,可以增加或减少元素的数量。

cpp 复制代码
vector<int> v = {1, 2, 3};

// 增加容器的大小为 5,新增的元素用默认值填充
v.resize(5);

// 减少容器的大小为 2,多余的元素将被删除
v.resize(2);

在上面的代码中,resize() 函数接受一个整数参数,表示要调整的大小。如果调整后的大小比当前大小大,则新增的元素将用默认值填充;如果调整后的大小比当前大小小,则多余的元素将被删除。

🍀reserve()

reserve() 函数用于预分配 vector 的内存空间,以提高性能。

cpp 复制代码
vector<int> v;

// 预分配至少能容纳 100 个元素的内存空间
v.reserve(100);

在上面的代码中,reserve() 函数接受一个整数参数,表示要预分配的内存空间大小。这样做可以避免频繁的内存重新分配,提高 vector 的性能。

需要注意的是,resize() 和 reserve() 函数都会影响 vector 的大小和容量。大小是指容器中实际存储的元素数量,而容量是指容器当前分配的内存空间大小。


🛎️感谢各位同伴的支持,本期C++专题就讲解到这啦,下期我们将进入模拟实现vector,如果你觉得写的不错的话,可以给个一键三连,点赞,收藏+评论,可以的话还希望点点关注,若有不足,欢迎各位在评论区讨论。

相关推荐
java1234_小锋4 分钟前
MyBatis如何处理延迟加载?
java·开发语言
FeboReigns21 分钟前
C++简明教程(4)(Hello World)
c语言·c++
FeboReigns22 分钟前
C++简明教程(10)(初识类)
c语言·开发语言·c++
学前端的小朱23 分钟前
处理字体图标、js、html及其他资源
开发语言·javascript·webpack·html·打包工具
zh路西法31 分钟前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式
.Vcoistnt1 小时前
Codeforces Round 994 (Div. 2)(A-D)
数据结构·c++·算法·贪心算法·动态规划
小k_不小1 小时前
C++面试八股文:指针与引用的区别
c++·面试
摇光931 小时前
js高阶-async与事件循环
开发语言·javascript·事件循环·宏任务·微任务
沐泽Mu1 小时前
嵌入式学习-QT-Day07
c++·qt·学习·命令模式
沐泽Mu1 小时前
嵌入式学习-QT-Day09
开发语言·qt·学习