C++基础——vector的详解与运用

devtools/2024/9/22 20:22:42/

vector的介绍

文档介绍

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

vector的使用

下面是对于vector的基本用法的讲解

vector的初始化

(constructor)构造函数声明接口说明
vector()(重点)无参构造
vector(size_type n, const value_type& val = value_type())构造并初始化n个val
vector (const vector& x); (重点)拷贝构造
vector (InputIterator first, InputIterator last);使用迭代器进行初始化构造

主要使用的是无参构造和拷贝构造,其他两种不常用,下面代码一并介绍

void test1()
{//1.vector()(重点) | 无参构造 vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto c : v1){cout << c << " ";}cout << endl;//2.vector(size_type n, const value_type& val = value_type()) | 构造并初始化n个valvector<int> v2(10, 0);for (auto c : v2){cout << c << " ";}cout << endl;//3.vector(const vector & x); (重点) | 拷贝构造        vector<int> v3(v1);for (auto c : v3){cout << c << " ";}cout << endl;//4.vector(InputIterator first, InputIterator last);vector<int> v4(v2.begin(), v2.end());for (auto c : v4){cout << c << " ";}cout << endl;
}
int main()
{test1();return 0;
}

vector iterator迭代器的使用

vector的迭代器有两种,正向和反向迭代器

正向:begin() 和 end() 分别表示获取第一个数据的位置,获取最后一个数据的下一个位置

反向:rbegin() 和 rend() 分别表示获取最后一个数据的位置,获取第一个元素的前一个位置

//迭代器的名称分别为  iterator  和reverse_iterator
void test2()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;//auto rit=v.rbegin();vector<int>::reverse_iterator rit = v.rbegin();while (rit != v.rend()){cout << *rit << " ";++rit;}cout << endl;for (auto c : v){cout << c << " ";}cout << endl;
}
int main()
{test2();return 0;
}

vector 空间增长问题

使用几种函数来管理和查看vector的空间

容量空间接口说明
size获取数据个数
capacity获取容量大小
empty判断是否为空
resize改变vector的size
reserve改变vector的capacity

主要是对于resize和reserve的使用,empty就是判别是否为空,size和capacity是查看当前vector的数据个数和容量

void test3()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);cout << v.size() << endl;cout << v.capacity() << endl;v.resize(20);cout << "更改size为20" << endl;cout << v.size() << endl;cout << v.capacity() << endl;v.reserve(40);cout << "更改capacity为40" << endl;cout << v.size() << endl;cout << v.capacity() << endl;v.resize(50);cout << "更改size为50" << endl;cout << v.size() << endl;cout << v.capacity() << endl;
}
void test4()
{vector<int> v;size_t ans = v.capacity();for (int i = 0; i < 200; i++){v.push_back(i);if (ans != v.capacity()){cout << "capacity为:" << ans << endl ;}ans = v.capacity();}
}int main()
{test4();return 0;
}

注意:capacity的增长倍数是由环境决定的,vs下在1.5倍数左右,在Linux下近乎2倍,且关于resize和reserve,我们只需要知道resize底层是调用reserve的,满足size<=capacity

vector的增删改查

vector的增删改查接口说明
push_back尾插
pop_back尾删
find查找指定的数据,返回下标(在算法模块,不在vector的成员函数中)
insert在pos位置上插入指定数值val
erase删除指定pos位置上的数据
swap交换两个vector对象的数据空间
operator[ ]和数组一样,通过下标方括号来访问vector的数据

STL分为两大模块,容器和算法,容器例如vector、list、set等,算法是在头文件<algorithm>中,是将容器中通用的函数归档在<algorithm>中,按照迭代器的不同,不同容器能使用相同或不同的函数。

push_back 和 pop_back 以及operator[ ]的使用

简单理解,不必做详解

void test5()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);//push_back的使用,传参val   const value_type& val  value_type实际就是T 模板类for (auto c : v){cout << c << " ";}cout << endl;//pop_back 的使用,不必传参,尾删v.pop_back();   for (auto c : v){cout << c << " ";}cout << endl;//operator[]的使用   可以访问指定下标位置元素,也可赋值cout << v[2] << endl;v[2] = 10;cout << v[2] << endl;
}
int main()
{test5();return 0;
}

find

在<algorithm>算法中,InputIterator find (InputIterator first, InputIterator last, const T& val);

#include<algorithm>
void test6()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);auto it =find(v.begin(), v.end(), 5);if (it != v.end()){cout << *it << endl;}else if(it == v.end()){cout << "没有找到val,返回的是v.end()" << endl;}
}
int main()
{test6();return 0;
}

insert

insert的插入,实际上就是先得到要插入的数值个数n,然后将vector中的元素后移n位,然后依次填入插入的数据

void test7()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto c : v){cout << c << " ";}cout << endl;//1.iterator insert (iterator position, const value_type& val); auto it=v.insert(v.begin() + 1, 10);cout << "返回迭代器指向的数值为:" << *it << endl;for (auto c : v){cout << c << " ";}cout << endl;//2.void insert (iterator position, size_type n, const value_type& val);v.insert(v.begin(), 5, 6);for (auto c : v){cout << c << " ";}cout << endl;//3.void insert(iterator position, InputIterator first, InputIterator last);v.insert(v.begin(), v.begin(), v.end());for (auto c : v){cout << c << " ";}cout << endl;vector<int> v1;  //后两个参数,只要是迭代器即可,可以是其他容器的迭代器数据插入过来v1.insert(v1.begin(), v.begin(), v.begin()+6);for (auto c : v1){cout << c << " ";}cout << endl;}
int main()
{test7();return 0;
}

erase

void test8()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//指定pos迭代器删除auto it =v.erase(v.begin());  //返回的是删除之后的v.begin()cout << *it << endl;for (auto c : v){cout << c << " ";}cout << endl;  //区间删除it=v.erase(v.begin() + 1, v.begin()+2);cout << *it << endl;		//返回的是删除之后的v.begin() + 1   但是要注意越界问题for (auto c : v){cout << c << " ";}cout << endl;
}
int main()
{test8();return 0;
}

迭代器失效

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,所以迭代器失效实际上就是迭代器底层对应指针所指向的空间被销毁了,而使用一窥啊已经倍释放的空间,当继续使用已经失效的迭代器,会造成程序崩溃。

迭代器失效的情景

  • 引起底层空间的改变都有可能是造成迭代器失效,如resize、reserve、insert、push_back等。他们的同意特性就是会在底层调用reserve来判断增加元素时,是否需要扩容。
  • 指定位置元素的删除操作,erase(pos)

第一种场景,统一导致迭代器失效的是,由于增加元素或者手动扩容,导致vector扩容,然而扩容机制,底层是新建一个T指针数组,拷贝原有数据到新数组中,然后释放原来的数组空间,所以迭代器会失效(此时的迭代器指向的是一块被释放的空间),导致程序崩溃

void test9()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//底层的改变实际上就是是否扩容了//先获得当前vector的首元素的迭代器vector<int>::iterator it = v.begin();  cout << *it << endl;//1.如果经历了resize ,触发扩容,迭代器失效    代码为 -1073741819。//v.resize(50, 0);//cout << *it << endl;//2.如果使用reserve手动扩容的话,也会导致迭代器失效//v.reserve(100);//cout << *it << endl;  //代码为 -1073741819。//3.如果是插入元素,导致扩容,也会导致迭代器失效//v.insert(v.begin(), 100, 1);//cout << *it << endl;    //代码为 -1073741819。//4.如果是push_back 导致扩容,也会发生迭代器失效for (int i = 0; i < 100; i++){v.push_back(i);}cout << *it << endl;   //代码为 -1073741819。
}
int main()
{test9();return 0;
}

第二种场景,erase函数,删除指定pos位置数据,或者删除迭代器区间的数据。我们先用auto it=v.begin()+5,获得迭代器,然后我们删除一个元素,删除之后有两种可能:1.it指向的位置还有元素,那么按照道理来说 cout一下还是能正常输出数值 2.it位置已经没有元素了,此时it指向的空间已经是vector.end()之后了,再次访问it的时候,就会报错,相当于越界了。

综合上述两种可能,因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

void test10()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);auto it = v.begin();v.erase(v.begin());//v.erase(v.begin(), v.end());cout << *it << endl;  //代码为 -1073741819
}
int main()
{test10();return 0;
}

迭代器失效解决办法:在使用前,对迭代器重新赋值即可。

总结

vector的优点:

1.支持随机访问(以[]的方式)

2.cpu高速缓存效率高

vector的缺点:

1.中间和头部删除元素效率低(要移动数组元素)

2.扩容问题(扩容会导致迭代器失效)

总结:

  • vector的使用实际上大部分的函数与string中的用法相似,我们只需要知道一点的是,vector是一个可变长的连续有序序列即可,不管是空间还是物理上,数据是连续的。
  • vector的初始的capacity的大小和初始化的数据个数有关,然后依次为基点,在VS平台以近似1.5倍扩容,在Linux下以2倍扩容。
  • 在vector中我们初步认识到了**的存在,这是算法,包含在STL中,STL分为容器和算法,**中是总结所有容器所需要的接口,通过迭代器来实现。不同的容器有不同的迭代器,能实现不同的算法接口。
  • 迭代器失效问题,是string以及vector在涉及到扩容或者erase中会发生的问题,但是erase返回参数为iterator,所以只需要再次赋值即可,而扩容导致的迭代器失效,我们只能再次对迭代器重新赋值,取得新的迭代器。随用随取,在使用前,对迭代器重新赋值。
  • 综上,我们可以通过vector,不必顾忌数组的长度问题,通过相应的便捷的接口,来实现更多可能性的操作。

http://www.ppmy.cn/devtools/43272.html

相关文章

电路仿真软件:点亮教学新篇章,十大便利助力高效学习

在信息化时代的浪潮中&#xff0c;电路仿真软件以其独特的优势&#xff0c;逐渐在教学领域崭露头角。它不仅能够帮助学生更好地理解电路知识&#xff0c;还能提升教师的教学效果。接下来&#xff0c;让我们一起探讨电路仿真软件对教学带来的十大便利。 一、直观展示电路原理 电…

使用 Effect 同步-09

有些组件需要与外部系统同步。例如&#xff0c;你可能希望根据 React state 控制非 React 组件、设置服务器连接或在组件出现在屏幕上时发送分析日志。Effects 会在渲染后运行一些代码&#xff0c;以便可以将组件与 React 之外的某些系统同步。 简单理解&#xff0c;就是需要操…

2024年03月 Python(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,共50分) 第1题 在Python中,hex(2023)的功能是?( ) A:将十进制数2023转化成十六进制数 B:将十进制数2023转化成八进制数 C:将十六进制数2023转化成十进制数 D:将八进制数2023转化成十进制数 答案:A …

【网络安全】勒索软件ShrinkLocker使用 windows系统安全工具BitLocker实施攻击

文章目录 威胁无不不在BitLocker 概述如何利用BitLocker进行攻击如何降低影响Win11 24H2 装机默认开启 BitLocker推荐阅读 威胁无不不在 网络攻击的形式不断发展&#xff0c;即便是合法的 Windows 安全功能也会成为黑客的攻击工具。 卡巴斯基实验室专家 发现 使用BitLocker的…

如何学习计算机网络(超详细,方法论)

分享一下学习计算机网络的方法论 首先是看视频&#xff1a; 这里我推荐中科大郑烇、杨坚全套《计算机网络&#xff08;自顶向下方法 第7版》课程 课程目标_哔哩哔哩_bilibili 教材采用神书《计算机网络&#xff08;自顶向下方法&#xff09;》&#xff0c;授课风格更偏向实…

代码随想录算法训练营第四天| 24.两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II

24.两两交换链表中的节点 给定一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后的链表。你不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 解题思路 很麻烦的一道题目&#xff0c;不是很理解。还是看视频文章才AC的。 解法1 …

OpenWrt 23.05 安装之后默认空间小 磁盘扩容 教程 软路由实测 系列六

1 安装fdisk opkg update opkg install fdisk #查看磁盘 rootOpenWrt:~# fdisk -l GPT PMBR size mismatch (246303 ! 250069679) will be corrected by write. The backup GPT table is not on the end of the device. Disk /dev/sda: 119.24 GiB, 128035676160 bytes, 25006…

C语言 | Leetcode C语言题解之第116题填充每个节点的下一个右侧节点指针

题目&#xff1a; 题解&#xff1a; struct Node* connect(struct Node* root) {if (root NULL) {return root;}// 从根节点开始struct Node* leftmost root;while (leftmost->left ! NULL) {// 遍历这一层节点组织成的链表&#xff0c;为下一层的节点更新 next 指针stru…