【C++STL详解(三)】------vector的介绍与使用

目录

前言

一、关于数组

二、vector的介绍

三、vector的使用

Ⅰ、默认成员函数

1.构造函数

2.赋值重载

3.析构函数

Ⅱ、容量

1.size()

2.capacity()

3.empty()

4.resize()

5.reserve()

Ⅲ、遍历操作

1.迭代器

begin() +end()(正向迭代器)

rbegin()+rend()(反向迭代器)

2.operator【】

Ⅳ、增删查改

1.push_back()

2.pop_back()

3.find()

4.insert

5.erase

6.swap

四、vector细节问题-迭代器失效

Ⅰ、失效案例

1.会引起底层结构发生改变的操作!

2.指定位置元素的删除操作--erase

Ⅱ、解决方案

Ⅲ、总结


前言

下面接着来介绍C++中容器vector的使用,本文章重点介绍一些常用的接口不是全部接口哦,完整版文档大家可以参照vector文档(建议PC端打开哦!)

一、关于数组

想必在C语言阶段是大家经常使用的数组都是静态的,就像下面这段代码一样

int a[10]={1,2,3,4,5,6,7,8,9,10};

上面就是我们平时喜欢定义的数组,实际上在C++STL库里面也存在着一个容器,array,只不过它是C++11后才提出的,它的出现主要是类比于上面这段数组!

这是它在文档中的介绍,它是一个模板类,参数是非类型模板参数,下面的解释是:它是一个固定大小的序列容器:它们按照严格的线性顺序保存特定数量的元素。说白了就是一个静态数组!

那么它和我们C语言中的静态数组有什么区别呢?其实就是对越界的处理方式不同,来看这两段代码

int a[10];//C语言中的静态数组
array<int,10> a2;//C++的//越界访问是否可行?
a[10];
a[11]=11;//可行?
a2[12];
a2[12]=1;

其实将其放入编译器中C语言的a数组,没有问题正常运行,而C++的这个a2数组程序会直接崩溃!

原因:

①对于C语言的a数组,编译器对其的处理是:越界读没有问题,越界写只是一种抽查,不一定会报错!具有局限性!

②而对于C++提供的array这个容器,对于任意位置的越界读写,程序都会直接结束,它的底层实现用了assert断言!

单从这个角度,C++提供的这个容器很香喷喷,其实不然,它存在的问题就是----开空间过大,会导致栈溢出!而且还不能初始化!又不香了!所以实际中我们一般喜欢使用vector容器,因为它能解决的vector也能解决,vector能解决它不一定能解决!

二、vector的介绍

vector是表示可变大小数组序列容器
就像数组一样,vector也采用的连续存储空间来存储元素,也就是说它可以采用下标对元素进行访问,唯一一点与数组的不同在vector它是动态的,大小会自动的去调整
本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是 一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
与其它动态序列容器相比(deque, list and forward_list),vector在访问元素的时候更加高效,在末 尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起listforward_list 统一的迭代器和引用更好
ps:这些容器后面都会讲解

三、vector的使用

Ⅰ、默认成员函数

1.构造函数

vector的构造函数常见的有四个:

vector();    //无参构造
vector(size_type n, const value_type& val = value_type());  //构造并初始化n个val
vector (const vector& x);//拷贝构造
vector (InputIterator first, InputIterator last);//迭代器区间构造

具体使用:

vector<int> v1;//无参vector<int> v2(10, 1);//初始化元素为10个1vector<int> v3(v2);//将v2的内容拷贝给v3,并初始化v3vector<int> v4(v2.begin(), v2.begin() + 5);//迭代器区间构造

2.赋值重载

vector& operator= (const vector& x);//使用
vector<int> v2(10, 1);//初始化元素为10个1vector<int> v5=v2;//赋值

3.析构函数

~vector();

一般这个采用自带的就行!

Ⅱ、容量

1.size()

//原型,获取数据个数
size_type size() const;//使用
vector<int> v2(10,1);
cout << v2.size();

2.capacity()

v2.capacity();//获取容量大小

3.empty()

v2.empty();//判空

4.resize()

void resize(size_type n, value_type val = value_type());

调整size的大小,规则如下:

①如果n比当前的size小,那么就会缩小当前的size直至和n相等,其余部分删除

②如果n比当前的size大,那么就会扩充到n,扩充的部分如果没有给出具体的值,则用0代替,反之用具体的值代替!

③如果n大于当前的capacity,则会自动重新分配空间,在将原始数据拷贝到新空间!

①比size小的情况:

②比size大的情况:

5.reserve()

void reserve(size_type n);

调整capacity的大小,规则如下:

①如果n小于当前容量,什么都不做

②如果n大于当前容量,就扩充到和n一样大

Ⅲ、遍历操作

1.迭代器

  • begin() +end()(正向迭代器)

实际上和前面的string类一样的迭代器!

begin():指向的是第一个元素的位置

 iterator begin(); 
 const_iterator begin() const;//为const对象提供的

end(): 指向的是最后一个元素的下一个位置!

iterator end();

const_iterator end() const;

代码实现:

  • rbegin()+rend()(反向迭代器)

rbegin:指向最后一个元素的位置!

reverse_iterator rbegin();

const_reverse_iterator rbegin() const;

rend:指向的是第一个元素的前一个位置!

reverse_iterator rend();

const_reverse_iterator rend() const;

代码实现:

2.operator【】

有了它就能想普通数组那样通过下标去访问特定的元素了!

reference operator[] (size_type n);

const_reference operator[] (size_type n) const;

直接看代码,没啥好说的哥们!

要注意:有了它我们不仅仅是读元素,还可以进行修改与赋值!

Ⅳ、增删查改

1.push_back()

一个简单的尾插操作!

void push_back(const value_type & val);

vector<int> v;
for (int i = 1; i <= 9; i++)
{v.push_back(i);
}

2.pop_back()

简单的尾删操作!

 void pop_back();
 

vector<int> v;
for (int i = 1; i <= 9; i++)
{v.push_back(i);
}v.pop_back();//尾删

3.find()

查找操作!需要注意:这个不是vector成员函数的接口,这个是算法库里面的,把它放在这里是因为它可以和下面的insert、erase操作配合使用!

  它在算法库面实际上是一个模板

可以看到它的功能是:在一个迭代区间里面去寻找特定的val,如果找到这个元素,那就返回当前位置的迭代器;如果找不到,那就返回迭代区间的最后一个位置迭代器!

4.insert

插入操作:在pos位置之前插入一个元素!

可以看到它需要传入对应位置的迭代器!所以就可以配合find()去使用!

直接看操作

①特定位置前插入一个值

②特定位置前插入n个val

③在特定位置前插入一段迭代区间(左闭右开)

一定要注意区间是左闭右开的。vector的动态数组下标都是从0开始的

5.erase

删除操作:删除特定位置或者区间的值!

iterator erase(iterator position);
iterator erase(iterator first, iterator last);

来吧,展示!同样也需要配合find()使用

①删除特定位置的值

②删除特定区间的值(左闭右开)

6.swap

主要是交换两个vector的数据空间!这个是vector内部的成员函数,不是那个算法库里面的全局的vector

    void swap(vector & x);
 

直接上代码:

四、vector细节问题-迭代器失效

首先需要知道一点,迭代器的作用就是让算法不关心底层的设计实现,而行为却像指针那样进行数据的访问和操作。对于vector来说,它的迭代器的底层设计就是原生的指针T*。所以迭代器失效就是指:迭代器底层对应指针所指向的空间被销毁,而却还在使用这块已经释放的空间,最终的结果会导致程序崩溃!

Ⅰ、失效案例

1.会引起底层结构发生改变的操作!

比如:resize、reserve、insert、assgin、push_back等,因为这些操作都可能会存在扩容这一操作!

vector<int> v;
for (int i = 1; i <= 9; i++)
{v.push_back(i);
}vector<int>::iterator it;
it = find(v.begin(), v.end(), 5);v.insert(it, 100, 1);while (it != v.end())
{cout << *it << " ";++it;
}//可行?

结果如下:

很明显,程序崩溃了!原因就是insert操作导致vector扩容了,开辟了新空间,也就是说vector旧的底层空间已经释放,可是it仍然指向那块空间,在对它进行访问时,实际上就是非法访问一块已经释放的空间,那程序必然崩溃!

2.指定位置元素的删除操作--erase

vector<int> v;
for (int i = 1; i <= 5; i++)
{v.push_back(i);
}vector<int>::iterator it;
it = find(v.begin(), v.end(), 3);v.erase(it);
while (it != v.end())
{cout << *it << endl;it++;
}

直接上结果:

和上面一样的结局,但是原因不同。erase它实际上是不改变底层的结构的,因为删除pos位置的元素后,pos位置后面的元素会向前挪动,理论上来讲是不会失效的!但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效 了。

Ⅱ、解决方案

最好的解决方案就是在上述操作的后面不要再使用迭代器了,如果硬是要使用的话,那应该要将迭代器进行更新了再去使用即可!

Ⅲ、总结

  • 迭代器失效的情况在不同的环境下处理结果不一样,上述崩溃的情况是在VS的下的情况,实际上在Linux下没有那么的严格,但是还是应该注意,要用前最好重新去赋值
  • 对于迭代器失效的问题,并不是所有的容器都会存在这个问题,迭代器的失效取决于该容器的底层结构是怎么样的,比如list的insert就没有这样的问题,因为链表底层是不连续的,new出来的,但是它的erase就有存在失效的问题!注意:链表的迭代器失效只会导致当前节点的迭代器失效!(了解底层后就可以理解了)

今天就分享到这里,希望对大家有所帮助!

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

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

相关文章

element 分页切换时:current-page无效 页数不会跟着一起切换

问题回溯&#xff1a;使用el-pagination组件 选择切换当前分页 页数为2 问题结果&#xff1a;el-pagination组件 当前页切换失败 一直都是 1&#xff0c;接口传参分页数据是2&#xff0c;打印当前分页也是2 解决方案1&#xff1a;使用 current-page参数 .sync 修饰符 解决方案2…

rust将json字符串直接转为map对象或者hashmap对象

有些时候我们还真的不清楚返回的json数据里面到底有哪些数据&#xff0c;数据类型是什么等&#xff0c;这个时候就可以使用批处理的方式将json字符串转为一个对象&#xff0c;然后通过这个对象的get方法来获取json里面的数据。 pub async fn test_json(&self) {let json_st…

STM32的TIM输入捕获和PWMI详解

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. IC输入捕获 2. 频率测量 3. 主模式、从模式、触发源选择 4. 输入捕获基本结构 5. PWMI模式 6. 代码示例 6.1 PWM.c 6.2 PWM.h 6.3 IC.c 6.4 IC.h 6.5 完整工程文件 输出比较可以看下面这篇…

numpy+matplotlib绘制阿基米德螺线

【第10次课]实验十一数据可视化及应用】 声明&#xff1a;著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 绘制阿基米德螺线&#xff0c;运行效果如图所示。 参数方程为: x icosi yisini 其中半径i和圆心角i变化一致&#xff0c;取值范围为…

#你觉得未来的智慧工地是什么样子的#

#你觉得未来的智慧工地是什么样子的# 有人说“现阶段的智慧工地都是噱头&#xff0c;实际用处不大”&#xff1b;也有人将智慧工地吹嘘上天。那么&#xff0c;随着技术的发展&#xff0c;你觉得未来的智慧工地会是什么样子呢&#xff1f; 随着大数据时代的到来&#xff0c;未来…

Three.js杂记(十五)—— 汽车展览(下)

在上一篇文章Three.js杂记&#xff08;十四&#xff09;—— 汽车展览上 - 掘金 (juejin.cn)中主要对切换相机不同位置和鼠标拖拽移动相机焦点做了简单的应用。 那么现在聊聊该如何实现汽车模型自带的三种动画展示了&#xff0c;实际上可以是两种汽车前后盖打开和汽车4车门打开…

抑郁后的症状表现——XWX-QP大小鼠强迫游泳桶硬件

简单介绍&#xff1a; 大小鼠强迫游泳桶硬件主要用于抗抑郁的研究。适用于大鼠、小鼠或其他实验室动物&#xff0c;通过将实验动物置于一个局限的环境中&#xff0c;动物在该环境中拼命挣扎试图逃跑又无法逃脱&#xff0c;从而提供了一个无可回避的压迫环境&#xff0c;动物的…

如何提取二维码文本信息?文本二维码提取内容的方法

如何分解出二维码中的文本信息呢&#xff1f;很多商家在做活动时会给每个用户生成一个单独的二维码&#xff0c;每一个二维码中有单独的编号信息&#xff0c;那么当我们收集到用户的二维码时&#xff0c;如何操作才能够提取二维码中的编号信息呢&#xff1f;想要解决这个问题可…

双目深度估计原理立体视觉

双目深度估计原理&立体视觉 0. 写在前面1. 双目估计的大致步骤2. 理想双目系统的深度估计公式推导3. 双目标定公式推导4. 极线校正理论推导 0. 写在前面 双目深度估计是通过两个相机的对同一个点的视差来得到给该点的深度。 标准系统的双目深度估计的公式推导需要满足:1)两…

Vue3+ts(day04:watch、watchEffect)

学习源码可以看我的个人前端学习笔记 (github.com):qdxzw/frontlearningNotes 觉得有帮助的同学&#xff0c;可以点心心支持一下哈&#xff08;笔记是根据b站上学习的尚硅谷的前端视频【张天禹老师】&#xff0c;记录一下学习笔记&#xff0c;用于自己复盘&#xff0c;有需要学…

树莓派5用docker运行Ollama3

书接上回&#xff0c;树莓派5使用1panel安装 Ollama 点击终端就可以进入容器 输入以下代码 ollama run llama3Llama3 是市场推崇的版本。您的 树莓派5上必须至少有 4.7GB 的可用空间&#xff0c;因此用树莓派玩机器学习就必须配置大容量的固态硬盘。用1panel部署网络下载速度…

Python | Leetcode Python题解之第58题最后一个单词的长度

题目&#xff1a; 题解&#xff1a; class Solution:def lengthOfLastWord(self, s: str) -> int:ls[]for i in s.split():ls.append(i)return len(ls[-1])

跟TED演讲学英文:The future will be shaped by optimists by Kevin Kelly

The future will be shaped by optimists Link: https://www.ted.com/talks/kevin_kelly_the_future_will_be_shaped_by_optimists Speaker: Kevin Kelly Date: August 2021 文章目录 The future will be shaped by optimistsIntroductionVocabularyTranscriptSummary后记 In…

Verilog基础语法——状态机(类型、写法、状态编码方式)

Verilog基础语法——状态机&#xff08;类型、写法、状态编码方式&#xff09; 写在前面一、状态机类型二、状态机写法2.1 一段式2.2 两段式2.3 三段式 三、状态机状态编码方式写在后面 写在前面 在FPGA设计过程&#xff0c;经常会设计状态机用于控制整个硬件电路的工作进程&am…

基于Vue3的Axios异步请求

基于Vue3的Axios异步请求 1. Axios安装与应用2. Axios网络请求封装3. axios网络请求跨域前端解决方案server.proxy 1. Axios安装与应用 Axios是一个基于promise的网络请求库&#xff0c;Axios.js.中文文档&#xff1a;https://axios.js.cn/ 安装&#xff1a;npm install --sa…

有没有一种可能性,你不投递简历,让HR主动联系你

你是否觉得自己得主动给某个公司投递了简历,他们才会联系你,亦或者是自己得主动在招聘APP上联系那个BOSS,他才会反过来跟你说话,又或者是你千方百计的跟他打招呼了,还是没有回应,这一节有可能让你明白,有时候是可以,你不主动,他也会主动联系你的。 目录 1 简历是如何…

QT:小项目:登录界面 (下一个连接数据库)

一、效果图 登录后&#xff1a; 二、项目工程结构 三、登录界面UI设计 四主界面 四、源码设计 login.h #ifndef LOGIN_H #define LOGIN_H#include <QDialog>namespace Ui { class login; }class login : public QDialog {Q_OBJECTpublic:explicit login(QWidge…

Spark原理之Cache Table的工作原理及实现自动缓存重复表的思考

CACHE TABLE的能力 使用此语法&#xff0c;可以由用户自定义要缓存的结果集&#xff0c;实际上就是一个临时表&#xff0c;不过数据存储在Spark集群内部&#xff0c;由Application所分配的executors管理。 一旦定义了一个缓存表&#xff0c;就可以在SQL脚本中随处引用这个表名…

Ansible自动化运维工具主机清单配置

作者主页&#xff1a;点击&#xff01; Ansible专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月24日12点21分 Ansible主机清单文件用于定义要管理的主机及其相关信息。它是Ansible的核心配置文件之一&#xff0c;用于Ansible识别目标主机并与其建立连接。 …

小猫咪邮件在线发送系统源码v1.1,支持添加附件

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 小猫咪邮件在线发送系统源码v1.1&#xff0c;支持添加附件 一款免登录发送邮件&#xff0c;支持发送附件&#xff0c;后台可添加邮箱,前台可选择发送邮箱 网站数据采取本地保存&…