C++STL之vector(超详细)

C++STL之vector

  • 1.vector基本介绍
  • 2.vector重要接口
    • 2.1.构造函数
    • 2.2.迭代器
    • 2.3.空间
      • 2.3.1.resize
      • 2.3.2.capacity
    • 2.4.增删查找
  • 3.迭代器失效
  • 4.迭代器分类

🌟🌟hello,各位读者大大们你们好呀🌟🌟
🚀🚀系列专栏:【C++的学习】
📝📝本篇内容:vector基本介绍;vector重要接口:构造函数;迭代器;空间;增删查改;迭代器失效;迭代器分类
⬆⬆⬆⬆上一篇:C++IO流
💖💖作者简介:轩情吖,请多多指教(> •̀֊•́ ) ̖́-

1.vector基本介绍

① 在我们C语言中有数组,C++中因此也设计了array来代替C语言中的数组,但是它有一个缺点,就是array是静态空间,一旦配置了空间就无法改变,这就让使用者非常麻烦。但是vector就不一样,它是动态空间,一旦底层空间不足,就会自动扩容,压根不需要担心空间不足而造成问题。
②vector扩容:当新元素插入进来时,发现内存不足,这个时候就会先扩容开辟新的空间(并不是原地扩容,因为可能后面没有内存),然后把原来的数据拷贝过去,再进行插入,然后把旧的空间给释放掉。并且我们扩容的空间基本上是以倍数来增长的,保证后续再有元素插入时,不需要扩容,导致效率低下。
③vector支持随机访问,即像数组一样[ ]来访问,非常高效,同时在末尾删除和末尾插入元素非常高效,这得益于它的结构。但是其他的位置进行操作效率就会比较低下,没有list好

2.vector重要接口

☞vector参考文档

2.1.构造函数

对于任何容器,首先看的肯定是构造函数
在这里插入图片描述
讲一下其中比较重要的

vector构造函数
vector()默认构造函数
vector(const vector&)拷贝构造
vector(size_type n,const value_type&val)构造n个val
vector(InputIterator first, InputIterator last)使用迭代器构造
#include <vector>
#include <iostream>
using namespace std;
int main()
{vector<int> v1;//默认构造int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };vector<int> v2(arr, arr + 10);//使用迭代器来构造for (auto& e : v2){cout << e << " ";}cout << endl;vector<int> v3(2, 10);//构造2个为10的元素for (auto& e : v3){cout << e <<" ";}vector<int> v4(v3);//拷贝构造return 0;
}

在这里插入图片描述

2.2.迭代器

我们来看一下迭代器,迭代器就是迭代器是一种访问容器内元素的对象,它提供了一种方法来顺序访问容器中的各个元素,而不需要了解容器的内部工作原理。

vector迭代器iterator/reverse_iterator
begin+end正向迭代器
rbegin+rend反向迭代器
#include <iostream>
#include <vector>
using namespace std;
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };vector<int> v(arr, arr + 10);//使用迭代器//正向迭代器vector<int>::iterator it = v.begin();while (it != v.end())//end是最后一个元素的下一个位置{cout << *it <<' ';//像指针一样使用it++;}cout << endl;//反向迭代器vector<int>::reverse_iterator rit = v.rbegin();while (rit != v.rend()){cout << *rit << ' ';//像指针一样使用rit++;//这里是++,而不是--,因为已经说明是反向迭代器,就该使用++}return 0;
}

在这里插入图片描述

2.3.空间

vector空间
size_type size() const;获取数据个数
size_type capacity() const;获取vector的容量大小
bool empty() const;vector是否为空
void reserve (size_type n);提前开辟空间,只会改变vector的capacity,能够缓解vector增容代价问题
void resize (size_type n, value_type val = value_type());改变vector的size,可以变大也可以变小
#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<int> v;//emptyif (v.empty()){cout << "empty" << endl;}cout << "------------------" << endl;//sizev.push_back(10);v.push_back(10);v.push_back(10);cout << v.size() << endl;cout << "------------------" << endl;//capacitycout << v.capacity() << endl;cout << "------------------" << endl;//reversev.reserve(10);//提前扩容到能存放10个元素的空间cout << v.size() << endl;//3,不会改变cout << v.capacity() << endl;//10cout << "------------------" << endl;//resizev.resize(20);cout << v.size() << endl;//20,改变size,元素为默认值cout << v.capacity() << endl;//20return 0;
}

在这里插入图片描述

2.3.1.resize

其中我们的resize还有其他的功能,可以缩小空间以及设定自己想要的元素,resize在开空间的同时还会进行初始化,影响size
具体演示见下面

#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<int> v;v.push_back(10);v.push_back(11);v.push_back(12);v.push_back(13);v.push_back(14);cout <<"size:"<<v.size() << endl;for (auto& e : v){cout << e << " ";}cout << endl;cout << "---------------------" << endl;v.resize(3);cout << "size:" << v.size() << endl;for (auto& e : v){cout << e << " ";}cout << endl;cout << "---------------------" << endl;v.resize(10,33);//把7个元素都设置为33cout << "size:" << v.size() << endl;for (auto& e : v){cout << e << " ";}cout << endl;cout << "---------------------" << endl;return 0;
}

在这里插入图片描述

2.3.2.capacity

我们这边再详细讨论一下capacity这个成员函数,我们想一下,我们的vector的扩容是按照什么来的呢?需要空间就扩容?一个个扩容?我们来看一下下面的一段代码,分别在windows和Linux下展示

#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<int> v;size_t cap = v.capacity();cout <<"initital capacity:"<< cap << endl;for (int i = 0; i < 100; i++){v.push_back(i);if (cap != v.capacity()){cap = v.capacity();cout << "capacity changed:" << cap << endl;}} return 0;
}

windows:
在这里插入图片描述

Linux:
在这里插入图片描述

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

我们还要注意下它的效率问题,如果我们提前知道需要多少空间,就可以提前扩容来保证一次的开辟空间,来避免多次扩容的效率低下

#include <iostream>
#include  <vector>
using namespace std;
int main()
{vector<int> v;v.reserve(100);//提前开辟好空间,防止一遍遍的扩容造成效率低下for (int i = 0; i < 100; i++){v.push_back(i);}cout << v.capacity() << endl;//100return 0;
}

2.4.增删查找

vector增删查改
void push_back (const value_type& val);尾插
void pop_back();尾删
InputIterator find (InputIterator first, InputIterator last, const T& val);这是算法中的,查找
iterator insert (iterator position, const value_type& val);在position前插入元素
void insert (iterator position, size_type n, const value_type& val);在position位置前插入n个val
iterator erase (iterator position);删除position位置的元素
iterator erase (const_iterator first, const_iterator last);删除迭代器范围的元素
void swap (vector& x);交换两个vector
reference operator[] (size_type n);像数组一样的访问
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{vector<int> v(2, 10);v.push_back(11);//尾插v.push_back(12);v.push_back(13);v.push_back(14);cout << "initiatl element:";for (auto& e : v){cout << e << " ";}cout << endl;cout << "------------------------------------------------------" << endl;v.pop_back();//尾删cout << "after pop_back():";for (auto& e : v){cout << e << " ";}cout << endl;cout << "------------------------------------------------------" << endl;v.insert(v.begin(), 9);//在第一个元素前的位置插入一个9cout << "after insert(v.begin(),9):";for (auto& e : v){cout << e << " ";}cout << endl;cout << "------------------------------------------------------" << endl;//find && erasevector<int>::iterator it=find(v.begin(), v.end(),12);//查找元素为12的位置if (it != v.end()){//如果返回的迭代器为end()说明没找到v.erase(it);}cout << "after erase(it):";for (auto& e : v){cout << e << " ";}cout << endl;cout << "------------------------------------------------------" << endl;//operator[]for (int i = 0; i < v.size(); i++){v[i] = i;//operator[]的返回值是引用}cout << "after change element through operator[]:";for (auto& e : v){cout << e << " ";}cout << endl;cout << "------------------------------------------------------" << endl;return 0;
}

在这里插入图片描述

3.迭代器失效

接下来我们要谈谈迭代器失效的问题了,这个如果不了解底层的话就会很容易掉进坑里面
首先我们先讲insert导致的迭代器失效
在这里插入图片描述
在我们使用insert的时候,如果空间不足了,那我们的vector就会自动帮我们扩容,但是扩容常常需要经历三个步骤:开辟新的空间;移动元素到新空间;释放原来的空间。我们vector底层的实现的迭代器本身就是指针,只不过是typedef了一下。仔细想想,问题就来了,当我们插入后,有概率空间不足,扩容后,原本的迭代器(指针)就是野指针了,没有指向新空间

//!!!!error code
#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<int> v;v.push_back(11);v.push_back(12);v.push_back(13);v.push_back(14);cout << v.capacity() << endl;//接下来再插入就会扩容	vector<int>::iterator it = v.begin();v.insert(it, 10);cout << v.capacity() << endl;cout << *it << endl;return 0;
}

上述代码就演示了这个情况,使用一块已经被释放的空间,造成的后果是程序崩溃,我们仅仅演示了一下insert造成的结果,其实其他会造成扩容的函数如resize,reverse,push_back都会造成迭代器失效,同时从另一个角度来说,我们所传入的迭代器position所指向的内容已经不是我们想要的了,这也是迭代器失效的一种情况,因此insert以后我们认为迭代器已经失效,不能再使用

下一种情况是erase,思考一下?erase会扩容吗?答案是并不会,只是删除元素,所以说就没问题了?不不不,仔细思考一下,我的迭代器此时指向最后一个元素,如果我erase了一个元素呢,我们底层的_finish指针就会–,那此时的迭代器就相等于是指向无效空间

//!!!!!error code
#include <iostream>
using namespace std;
#include <vector>
int main()
{int a[] = { 1, 2, 3, 4 };vector<int> v(a, a + sizeof(a) / sizeof(int));// 使用find查找3所在位置的iteratorvector<int>::iterator pos = find(v.begin(), v.end(), 3);// 删除pos位置的数据,导致pos迭代器失效。v.erase(pos);cout << *pos << endl; // 此处会导致非法访问return 0;
}

在这里插入图片描述

可以运行一下上面的代码分别在Linux和Windows下,在Windows下会崩溃,但是在Linux可以运行,这不得不说VS的检查非常严,erase后的迭代器是不允许使用的,但是g++的就会宽很多,当删除3后,元素4会往前移动,pos的位置还是有效的
接下来就看一下下面这个代码

#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<int> v{1,2,3,4,5};//删除偶数vector<int>::iterator it = v.begin();while (it != v.end()){if ((*it) % 2 == 0){v.erase(it);}it++;}for (auto& e : v){cout << e << " ";}cout << endl;return 0;
}

首先对于环境而言是一样的,在VS下会崩溃,而Linux下能正常运行
在这里插入图片描述
在这里插入图片描述
我们来分析一下这个代码
在这里插入图片描述
可以发现,我们的it很巧合的把偶数元素删除了,同时也正好落到了_finish使用空间的尾end(),也判断循环结束了,我们接下里对代码稍作修改如下

#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<int> v{ 1,2,3,4};//删除偶数vector<int>::iterator it = v.begin();while (it != v.end()){if ((*it) % 2 == 0){v.erase(it);}it++;}for (auto& e : v){cout << e << " ";}cout << endl;return 0;
}

我们的元素的数量变为只有四个,接下来再看一下它的执行逻辑
在这里插入图片描述
通过上面的图可以发现,我们的it直接和_finish(end())直接错过了,这样即使是Linux也无能为力了
在这里插入图片描述
直接发生段错误

从上述的例子中可以看到:SGI STL中,迭代器失效后,代码并不一定会崩溃,但是运行结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的。
因此我们的insert和erase函数都会返回一个迭代器来供我们使用,我们修正一下上面的代码

#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<int> v{ 1,2,3,4};//删除偶数vector<int>::iterator it = v.begin();while (it != v.end()){if ((*it) % 2 == 0){it=v.erase(it);//接受迭代器,返回的迭代器是it传入时的位置}else{it++;}}for (auto& e : v){cout << e << " ";}cout << endl;return 0;
}

我们的string其实也是一样的道理,只是对于string我们很少用迭代器
在这里插入图片描述
我们对str进行了提前扩容,造成了野指针问题,我们的string使用失效的迭代器也会崩溃

4.迭代器分类

在我们使用算法函数时,有些函数需要特定的迭代器,像我们的vector的迭代器本质就是原生指针,因此它是一个随机迭代器,我们可以看一下侯捷大佬的《STL源码剖析》里对迭代器的分类
在这里插入图片描述
在这里插入图片描述我们Forward迭代器就是单向迭代器,它是只支持重载++的迭代器,Bidrectional迭代器是双向迭代器,它支持++和–,而我们的Random是随机迭代器,它支持++和–,也支持+和-,同样支持[ ]
理论上来讲,模板语法上可以传任何类型参数,但是内部使用迭代器是有要求

🌸🌸C++STL之vector的知识大概就讲到这里啦,博主后续会继续更新更多C++的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入浅出机器学习中的梯度下降算法

大家好&#xff0c;在机器学习中&#xff0c;梯度下降算法&#xff08;Gradient Descent&#xff09;是一个重要的概念。它是一种优化算法&#xff0c;用于最小化目标函数&#xff0c;通常是损失函数。梯度下降可以帮助找到一个模型最优的参数&#xff0c;使得模型的预测更加准…

树莓派5+文心一言 -> 智能音箱

一、简介 效果&#xff1a;运行起来后&#xff0c;可以连续对话 硬件&#xff1a;树莓派5、麦克风、音箱&#xff0c;成本500-1000 软件&#xff1a;snowboy作为唤醒词、百度语音作为语音识别、brain作为指令匹配、百度文心一言作为对话模块、微软的edge-tts语音合成... 二…

Springboot——SseEmitter流式输出

文章目录 前言SseEmitter 简介测试demo注意点异常一 ResponseBodyEmitter is already set complete 前言 最近做AI类的开发&#xff0c;看到各大AI模型的输出方式都是采取的一种EventStream的方式实现。 不是通常的等接口处理完成后&#xff0c;一次性返回。 而是片段式的处理…

5G学习笔记之随机接入

目录 1. 概述 2. MSG1 2.1 选择SSB 2.2 选择Preamble Index 2.3 选择发送Preamble的时频资源 2.4 确定RA-RNTI 2.5 确定发送功率 3. MSG2 4. MSG3 5. MSG4 6. 其它 6.1 切换中的随机接入 6.2 SI请求的随机接入 6.3 通过PDCCH order重新建立同步 1. 概述 随机接入…

【Linux-多线程】重谈地址空间+内存管理方式

一、背景知识 a.重谈地址空间 我们之前已经说过&#xff0c;CPU内部见的地址&#xff0c;以及我们打印出来的地址都是虚拟地址&#xff1b;物理内存加载到CPU&#xff0c;CPU内执行进程创建内核数据结构&#xff0c;页表等&#xff0c;通过页表映射到物理磁盘上&#xff1b;也…

Spark Optimization —— Reducing Shuffle

Spark Optimization : Reducing Shuffle “Shuffling is the only thing which Nature cannot undo.” — Arthur Eddington Shuffle Shuffle Shuffle I used to see people playing cards and using the word “Shuffle” even before I knew how to play it. Shuffling in c…

Elasticsearch——Java API 操作

Elasticsearch 软件是由Java语言开发的,所以也可以通过JavaAPI的方式对 Elasticsearch服务进行访问。 创建 Maven 项目 我们在 IDEA 开发工具中创建 Maven 项目(模块也可)ES。并修改pom文件&#xff0c;增加Maven依赖关系。 #直接复制在pom文件的<dependencies></de…

量化的8位LLM训练和推理使用bitsandbytes在AMD GPUs上

Quantized 8-bit LLM training and inference using bitsandbytes on AMD GPUs — ROCm Blogs 在这篇博客文章中&#xff0c;我们将介绍bitsandbytes的8位表示方式。正如你将看到的&#xff0c;bitsandbytes的8位表示方式显著地减少了微调和推理大语言模型&#xff08;LLMs&…

自回归(Autoregressive)模型概述

自回归&#xff08;Autoregressive&#xff09;模型概述 自回归&#xff08;Autoregressive&#xff0c;简称AR&#xff09;模型是一类基于“历史数据”来预测未来数据的模型。其核心思想是模型的输出不仅依赖于当前输入&#xff0c;还依赖于先前的输出。自回归模型通常用于时…

Win11电脑亮度无法调节以及夜间模式点击没有用失效解决方法

一、问题 最近&#xff0c;突然感觉屏幕亮度十分刺眼&#xff0c;想调整为夜间模式&#xff0c;发现点了夜间模式根本没用&#xff0c;亮度也是变成了灰色。 明明前几天还能调节的&#xff0c;这实在是太难受了&#xff01; 二、原因 这是远程控制软件向日葵的问题 在向日葵…

Linux笔记---进程:进程终止

1. 进程终止概念与分类 进程终止是指一个正在运行的进程结束其执行的操作。以下是一些常见的导致进程终止的情况&#xff1a; 一、正常终止 完成任务当进程完成了它被设计要执行的任务后&#xff0c;就会正常终止。收到特定信号在操作系统中&#xff0c;进程可能会收到来自操作…

【工具推荐】dnsx——一个快速、多用途的 DNS 查询工具

basic/基本使用方式 echo baidu.com | dnsx -recon # 查询域名所有记录echo baidu.com | dnsx -a -resp # 查询域名的a记录echo baidu.com | dnsx -txt -resp # 查询域名的TXT记录echo ip | dnsx -ptr -resp # ip反查域名 A记录查询 TXT记录查询 ip反查域名 help/帮助信息 输…

【树莓派5】移动热点获取树莓派IP并初次登录SSH

本篇文章包含的内容 1 打开系统热点2 烧录系统设置3 配置 MobaXterm4 初次启动树莓派配置选项4.1 换源4.2 更新软件包4.3 安装vim编辑器4.4 更改CPU FAN温度转速 Windows版本&#xff1a;Windows11 24H2树莓派&#xff1a;树莓派5&#xff0c;Raspberry Pi 5SSH软件&#xff1a…

【Git系列】Git 提交历史分析:深入理解`git log`命令

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

第144场双周赛:移除石头游戏、两个字符串得切换距离、零数组变换 Ⅲ、最多可收集的水果数目

Q1、[简单] 移除石头游戏 1、题目描述 Alice 和 Bob 在玩一个游戏&#xff0c;他们俩轮流从一堆石头中移除石头&#xff0c;Alice 先进行操作。 Alice 在第一次操作中移除 恰好 10 个石头。接下来的每次操作中&#xff0c;每位玩家移除的石头数 恰好 为另一位玩家上一次操作…

Python parsel库学习总结

parsel库是Python中用于解析HTML文件的库&#xff0c;其能通过CSS选择器、xpath、正则表达式来定位html中的元素。 通过css选择器定位元素 from parsel import Selectorhtml """ <html><head><a class"option1">这是一个伪html片…

【HarmonyOS学习日志(11)】计算机网络之概念,组成和功能

文章目录 计算机网络概念计算机网络&#xff0c;互连网与互联网的区别计算机网络互连网互联网&#xff08;因特网&#xff0c;Internet&#xff09; 计算机网络的组成和功能计算机网络的组成从组成部分看从工作方式看从逻辑功能看 计算机网络的功能数据通信资源共享分布式处理提…

Vue3 开源UI 框架推荐 (大全)

一 、前言 &#x1f4a5;这篇文章主要推荐了支持 Vue3 的开源 UI 框架&#xff0c;包括 web 端和移动端的多个框架&#xff0c;如 Element-Plus、Ant Design Vue 等 web 端框架&#xff0c;以及 Vant、NutUI 等移动端框架&#xff0c;并分别介绍了它们的特性和资源地址。&#…

视觉语言动作模型VLA的持续升级:从π0之参考基线Octo到OpenVLA、TinyVLA、DeeR-VLA、3D-VLA

第一部分 VLA模型π0之参考基线Octo 1.1 Octo的提出背景与其整体架构 1.1.1 Octo的提出背景与相关工作 许多研究使用从机器人收集的大量轨迹数据集来训练策略 从早期使用自主数据收集来扩展策略训练的工作[71,48,41,19-Robonet,27,30]到最近探索将现代基于transformer的策略…

k8s--pod创建、销毁流程

文章目录 一、pod创建流程二、pod销毁流程 一、pod创建流程 1、用户通过kubectl或其他api客户端提交pod spec给apiserver,然后会进行认证、鉴权、变更、校验等一系列过程2、apiserver将pod对象的相关信息最终存入etcd中,待写入操作执行完成,apiserver会返回确认信息给客户端3、…