【C++ STL】vector类最全详解(什么是vector?vector类的常用接口有哪些?)
目录
一、前言
二、什么是vector ?
💦 vector的基本概念
💦vector的作用是什么
💦总结
三、 vector的(一维)定义
四、vector(一维)常用接口的使用
💦vector的常见构造(初始化)
💦vector的遍历及迭代器的操作
① operator[ ]
② at ( )
③迭代器
③ 范围for
💦vector的常见容量操作
① size
② capacity
③ reserve(⭐)
④ resize(⭐)
⑤【reserve】和【resize】在使用中的易错点
⑥ empty
💦vector的常见访问操作
💦vector的常见修改操作
① push_back
② pop_back
③ insert
④ erase
⑤ swap
⑥ find
五、共勉
一、前言
最近在刷leetcode的时候,发现vector都还没弄明白吗,但是STL的强大是众所周知滴,早晚都是要解决滴,因此专门写下这篇文章,以供自己复习和各位老铁使用,快速的回忆vector的用法,让你找回自信,不用再竞赛的时候颜面尽失。
本次博客主要讲解vector的一维用法,由于篇幅过长,vector的二维用法,下一篇博客来阐述,请大家持续关注我O!!
二、什么是vector ?
向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。
💦 vector的基本概念
Vector的数据安排以及操作方式,与array(数组)非常相似,两者的唯一差别在于空间的运用的灵活性。
- Array是静态空间,一旦配置了就不能改变,要换大一点或者小一点的空间,可以,一切琐碎得由自己来,首先配置一块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间。
- Vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素。因此vector的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必害怕空间不足而一开始就要求一个大块头的array(数组)了。
Vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率,一旦vector旧空间满了,如果客户每新增一个元素,vector内部只是扩充一个元素的空间,实为不智,因为所谓的扩充空间(不论多大),一如刚所说,是”配置新空间-数据移动-释放旧空间”的大工程,时间成本很高,应该加入某种未雨绸缪的考虑,稍后我们便可以看到vector的空间配置策略。
💦vector的作用是什么
vector是C++标准模板库中的部分内容,中文偶尔译作“容器”,但并不准确。它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库。vector之所以被认为是一个容器,是因为它能够像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。
💦总结
- vector是表示可变大小数组的序列容器。
- 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
- vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
- 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
- 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好
三、 vector的(一维)定义
单独定义一个vector:
vector name;
上面这个定义其实相当于是一维数组name[size],只不过其size可以根据需要进行变化,这就是“变长数组”的名字的由来。
这里的typename可以是任何基本类型,例如int、double、char、结构体等,也可以是STL标准容器,例如string、set、queue、vector等。
注意:使用前必须加上头文件:
代码展示:
#include #include using namespace std; int main() { int a[3]; // 正常定义的----静态数组 vector str_a; // vector定义的----动态数组 char b[3]; vector str_b; return 0; }
效果展示:
四、vector(一维)常用接口的使用
💦vector的常见构造(初始化)
接口名称 接口说明 vector ();(⭐) 无参构造(构造一个没有元素的空容器,size = 0) vector (size_type n, const value_type& val = value_type()); 构造一个包含 n 个元素的容器,元素值为 val vector (const vector& x); (⭐) 拷贝构造 template vector (InputIterator first, InputIterator last);(函数模板) 使用迭代器进行初始化构造 [first,last) 注意: ⭐表示重点掌握
方式一: 构造一个某类型的空容器:
vector 函数名; 初始化为空。
vector v1; //构造int类型的空容器
方式二: 构造一个含有n个val的某类型容器:
vector 函数名(a,b).定义a个空间,都初始化为b。
vector v2(10, 2); //构造含有10个2的int类型容器
方式三: 拷贝构造某类型容器的复制品:
vector 函数名1(函数名2),把动态数据2复制给动态数组1vector v3(v2); //拷贝构造int类型的v2容器的复制品
方式四: 使用迭代器拷贝构造某一段内容:
vector 函数名1(函数名2.begin(),函数名2.end())把动态数组2复制给动态数组1。vector v4(v2.begin(), v2.end()); //使用迭代器拷贝构造v2容器的某一段内容
方式五:迭代器构造函数也可以使用数组来进行构造,传的区间是左闭右开
vector 函数名(a,a+sizeof(a)/sizeof(数据类型)),把普通数组a复制给动态数组。
注意:该方式也可用于拷贝其他容器的某一段内容。
string s("hello world"); vector v5(s.begin(), s.end()); //拷贝构造string对象的某一段内容
代码展示1(实用):
#include #include using namespace std; int main() { std::vector first; // 构造一个没有元素的空容器 std::vector second(2, 10); // 2个值为10的整数 std::vector third(second.begin(), second.end()); // 迭代器构造 std::vector fourth(third); // 拷贝构造 // 迭代器构造函数也可以使用数组来进行构造,传的区间是左闭右开 // 因为指向数组空间的指针是天然的迭代器 int arr[] = { 16,2,77,29 }; std::vector fifth(arr, arr + 4); // std::vector fifth (arr, arr + sizeof(arr) / sizeof(int) ); // first : [] // second: [10,10] // third : [10,10] // fourth: [10,10] // fifth : [16,2,77,29] return 0; }
效果展示:
代码展示2(不实用):void test2() { // 用其它容器的迭代器初始化,只要数据d类型可以匹配上 string s("hello"); vector v(s.begin(), s.end()); for (auto& e : v) { cout