C++STL详解(三)——vector类的接口详解

目录

一.vector的介绍

二.vector的构造以及赋值

2.1构造函数

 2.2operator=重载

三.vector的空间操作

3.1capacity和size函数

3.2reserve和resize函数

3.3empty函数

四.vector迭代器相关函数

4.1begin和end函数

 4.2rbegin和rend函数

五.vector的增删查改

5.1push_back和pop_back函数

5.2insert函数

 5.3erase函数

5.4find函数

5.5at和operator[]

六.vector的迭代器失效问题

6.1迭代器失效问题的举例

举例1

举例2

6.2迭代器失效问题的解决方法

6.3vs迭代器简析 

七.后记


一.vector的介绍

vector容器的底层是顺序表,在CPP中,我们对顺序表进行了一定程度的封装。

如果你不了解顺序表,那么可以浏览此片博文:顺序表

二.vector的构造以及赋值

2.1构造函数

在这里我们需要学习以下四个构造函数:

	vector();//空构造vector(size_type n, const value_type& val = value_type());//使用n个value构造vector(const vector & x);//拷贝构造vector(InputIterator first, InputIterator last);//迭代器区间构造(前闭后开)

构造1:构造一个空的vector容器

vector<int> v1;//空构造

构造2:构造含有n个val的vector容器

vector<int> v2(10,1);//十个1

 大家可能注意到了这个构造函数中的第二个类型是容器内的数值类型。而它给的缺省值是value_type();因此我们大家可能产生这样的疑惑:内置类型是如何处理的?

构造3:复制别的同类型容器构造

vector<int> v3(v2);//拷贝v2构造v3

构造4: 使用迭代器区间进行构造

vector<int> v4(v2.begin()+1,v2.end()-1);//使用迭代器构造

 2.2operator=重载

同样的,我们也可以使用重载后的operator=操作符来进行给一个空的vector容器赋值。

	vector<int> v6=v2;//将v2的值赋给v6

三.vector的空间操作

3.1capacity和size函数

我们在初始化一个vector容器之后,可以通过这两个函数查看该容器的空间大小和有效数据个数。

函数原型如下:

void capacity();
void size();

在我们用两个1初始化一个vector容器之后,可以使用这两个函数查看它的空间大小。 

3.2reserve和resize函数

和string相同的是,reserve函数的功能是设置空间大小,resize函数的作用是设置有效数据个数。

reserve:

  • 如果设置的空间大于当前capacity,则扩大空间至capacity或大于capacity。
  • 如果设置的空间小于当前capacity,则缩小空间或不做变化。

resize:

  • 如果有效数据个数大于当前size,若规定了特定值则用特定值填充,否则用数据类型的默认值填充
  • 如果有效数据个数小于当前size,则只保留前size个数据。

我们可以通过以下代码实践这两个函数:

void test2()
{vector<int> v1(2,1);cout << v1.capacity() << endl;//2cout << v1.size() << endl;//2v1.reserve(10);cout << v1.capacity() << endl;//10v1.reserve(2);cout << v1.capacity() << endl;//2v1.resize(4);cout << v1.size() << endl;//4v1.resize(2);cout << v1.size() << endl;//2v1.resize(4, 3);cout << v1.size() << endl;//4
}

我们可以把两次扩大size的结果打印出来,如下:

3.3empty函数

empty函数判断vector容器是否为空的函数。

empty:

  • 如果vector容器为空,则返回真
  • 如果vector容器不为空,则返回假。

我们可以写出如下测试代码:

	vector<int> v1(2,1);vector<int> v2;cout << v1.empty() << endl;//0-->假cout << v2.empty() << endl;//1-->真

四.vector迭代器相关函数

4.1begin和end函数

begin()函数返回vector容器的第一个位置

end()函数返回vector容器的最后一个位置的下一个位置。

图解如下:

我们可以使用这两个函数来遍历容器。

void test3()
{vector<int> v1(10, 3);v1.push_back(4);//正向迭代器vector<int>::iterator it1 = v1.begin();while (it1 != v1.end()){cout << *it1 << ' ';it1++;}cout<< endl;
}

值得注意的是,如果容器为空的话,则begin()和end()指向同一处空间。 

 4.2rbegin和rend函数

同样的,vector也给我们提供了反向迭代器rbegin和rend。

图解如下:

同样的,我们也可以使用这两个函数来遍历容器

	//反向迭代器vector<int>::reverse_iterator it2 = v1.rbegin();while (it2 != v1.rend()){cout << *it2 << ' ';it2++;}cout << endl;

 打印结果如下:

正向:3 3 3 3 3 3 3 3 3 3 4
反向:4 3 3 3 3 3 3 3 3 3 3

五.vector的增删查改

5.1push_back和pop_back函数

push_back函数是用来尾插的;

pop_back函数是用来尾删的。

void test4()
{vector<int> v1;v1.push_back(1);v1.push_back(1);v1.push_back(1);v1.push_back(1);print(v1);//为了方便观察,这个函数是自己写的。v1.pop_back();print(v1);
}

打印结果如下:

1 1 1 1
1 1 1

5.2insert函数

insert函数用于插入数据。

insert函数的函数原型如下:

iterator insert(const_iterator position, const value_type& val);//在position位置插入一个val
iterator insert(const_iterator position, size_type n, const value_type& val);//在positio位置插入n个val
iterator insert(const_iterator position, InputIterator first, InputIterator last);//将迭代区间[first,last)插入position

insert:

  • 第一个函数用于插入一个val。
  • 第二个函数用于插入n个val。
  • 第三个函数用于在某个位置插入一个迭代区间。 
void test5()
{vector<int> v1(10,1);vector<int> v2(2, 5);v1.insert(v1.begin(), 2);//在第一个位置插入一个2print(v1);// 2 1 1 1 1 1 1 1 1 1 1v1.insert(v1.begin() + 2, 3, 3);//在第3个位置插入3个3print(v1);//2 1 3 3 3 1 1 1 1 1 1 1 1 1v1.insert(v1.begin(), v2.begin(), v2.end());print(v1);//5 5 2 1 3 3 3 1 1 1 1 1 1 1 1 1
}

 5.3erase函数

 erase函数用于删除数据。

erase函数的原型如下:

iterator erase (const_iterator position);//删除position位置处
iterator erase (const_iterator first, const_iterator last);//删除迭代区间

 erase:

  • 第一个函数用于删除指定位置处的数据
  • 第二个函数用于删除迭代区间[first,last)的数据
void test5()
{vector<int> v1(10,1);vector<int> v2(2, 5);v1.insert(v1.begin(), 2);//在第一个位置插入一个2print(v1);// 2 1 1 1 1 1 1 1 1 1 1v1.insert(v1.begin() + 2, 3, 3);//在第3个位置插入3个3print(v1);//2 1 3 3 3 1 1 1 1 1 1 1 1 1v1.insert(v1.begin(), v2.begin(), v2.end());print(v1);//5 5 2 1 3 3 3 1 1 1 1 1 1 1 1 1v1.erase(v1.begin());print(v1); //5 2 1 3 3 3 1 1 1 1 1 1 1 1 1v1.erase(v1.begin()+1,v1.end()-1);print(v1);//5 1
}

5.4find函数

find函数不是vector库中的函数,而是算法库中的函数。

它的功能是在某个迭代器范围内寻找特定值。

算法库的头文件是:

#include <algorithm>

find函数的原型如下:

InputIterator find (InputIterator first, InputIterator last, const T& val);

 我们可以用如下代码使用find函数

	vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);vector<int>::iterator p=find(v1.begin(), v1.end(), 3);printf("&p",p);

结果:

0000001BCE97F580 

这里有一个需要大家注意的点:

我们无法使用cout打印出迭代器类型的数据,因此我们这里要用C原生的printf函数进行打印。

5.5at和operator[]

我们可以使用at和operator[]来进行访问数据。
如下所示:

void test6() 
{vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);int len = v1.size();vector<int> v(10, 1);for (size_t i = 0; i < len; i++){cout << v1[i] << " ";}cout << endl;for (size_t i = 0; i<len; i++){cout << v1[i] << ' ';}cout << endl;
}

结果:

1 2 3 4
1 2 3 4

六.vector的迭代器失效问题

迭代器的主要作用是让算法不用再关心底层的数据结构,其底层实际上就是一个指针,或者是对指针进行了封装,比如vector的迭代器的原生态指针T*

因此迭代器失效,其实就是迭代器对应的指针指向的内容被销毁了,使用一块被释放的空间的后果就是程序崩溃。

会引起其底层空间改变的操作,都有可能会造成迭代器失效。如:resize、reserve、insert、assign、push_back等。

6.1迭代器失效问题的举例

举例1
void test7()
{vector<int> v{ 1,2,3,4,5,6 };auto it = v.begin();// 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容v.resize(100, 8);// reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变v.reserve(100);// 插入元素期间,可能会引起扩容,而导致原空间被释放v.insert(v.begin(), 0);v.push_back(8);// 给vector重新赋值,可能会引起底层容量改变v.assign(100, 8);while (it != v.end()){cout << *it << " ";++it;}cout << endl;
}

由于我们在扩容的过程中需要经常使用到异地扩容,因此我们每次插入内容都可能会出现异地扩容,这也就导致了我们原来的迭代器失效。

也就是说,vector原来的空间已经被销毁了,我们的it指向一块被销毁的空间,再对it进行使用时,实际操作的就是一块已经被释放了的空间,从而造成了代码的崩溃。

举例2

vs2022环境下:(gcc环境下能跑)

void test8()
{int a[] = { 1, 2, 3, 4 };vector<int> v(a, a + sizeof(a) / sizeof(int));//a是首元素地址,这儿相当于[a,a+4);使用迭代器区间初始化。// 使用find查找3所在位置的iteratorvector<int>::iterator pos = find(v.begin(), v.end(), 3);// 删除pos位置的数据,导致pos迭代器失效。v.erase(pos);cout << *pos << endl; // 此处会导致非法访问
}

 erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效。

但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。

6.2迭代器失效问题的解决方法

为了删除vector容器中的偶数,现在给出如此一段代码:

void test9()
{vector<int> v{ 1, 2, 3, 4 };auto it = v.begin();while (it != v.end()){if (*it % 2 == 0){v.erase(it);}++it;}
}

在删除掉it位置处的数据后,vs就认为迭代器失效了。因此我们这段代码依旧是无法跑的过去的。那么我们应该如何修改这段代码以让他跑的过去呢?

我们发现,迭代器失效的原因是因为底层的指针变化,因此我们每次使用前只要给它再赋一个值即可。

因此,我们可以将代码修改为如下:

void test9()
{vector<int> v{ 1, 2, 3, 4 };auto it = v.begin();while (it != v.end()){if (*it % 2 == 0){it=v.erase(it);}else{it++;}}
}

6.3vs迭代器简析 

vs环境对迭代器失效的问题检查是远比gcc环境下严格的。

gcc环境下对迭代器做的处理比较简单,

而在vs环境下,在迭代器的底层中采取了打标志的做法。

即,类似于这样的一行代码:

bool flag=true;

在使用迭代器前,vs会检查flag的值,如果flag为false的话,则会报错。

如果对迭代器进行了写操作,那么就会将flag置false。

而我们每次给迭代器赋值,都会使flag置true。

因此,我们在gcc环境下的一些情况中即便不在对空间进行操作后赋值也不会有什么影响。

但是,为了代码不出现bug,我们应养成每次对空间进行操作后,都对要使用的迭代器进行赋值的习惯。

七.后记

有关vector的模拟实现可参考博主的下篇博文。

如果你想更深入的了解vector的相关内容,可参考cpp官网:cpp官网

码字不易,给个点赞收藏叭~~~

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

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

相关文章

web前端 React 框架面试200题(一)

面试题 1. 简述什么是React &#xff08; 概念 &#xff09;&#xff1f; 参考回答&#xff1a; 1、React是Facebook开发的一款JS库。 2、React一般被用来作为MVC中的V层&#xff0c;它不依赖其他任何的库&#xff0c;因此开发中&#xff0c;可以与任何其他的库集成使用&…

QT串口和数据库通信

创建串口 串口连接客户端并向服务器发送消息 client.pro #------------------------------------------------- # # Project created by QtCreator 2024-07-02T14:11:20 # #-------------------------------------------------QT core gui network QT core gui…

【React】箭头函数:现代 JavaScript 的高效编程方式

文章目录 一、箭头函数的基本语法二、箭头函数的特性三、在 React 中的常见用法四、最佳实践 在现代 JavaScript 中&#xff0c;箭头函数&#xff08;Arrow Functions&#xff09;是一种简洁的函数表达方式&#xff0c;并且在 React 开发中非常常见。箭头函数不仅简化了函数的语…

RockyLinux 9 PXE Server bios+uefi 自动化部署 RockLinux 8 9

pxe server 前言 PXE&#xff08;Preboot eXecution Environment&#xff0c;预启动执行环境&#xff09;是一种网络启动协议&#xff0c;允许计算机通过网络启动而不是使用本地硬盘。PXE服务器是实现这一功能的服务器&#xff0c;它提供了启动镜像和引导加载程序&#xff0c;…

前端开发知识(三)-javascript

javascript是一门跨平台、面向对象的脚本语言。 一、引入方式 1.内部脚本&#xff1a;使用<script> &#xff0c;可以放在任意位置&#xff0c;也可以有多个&#xff0c;一般是放在<body></body>的下方。 2.外部脚本&#xff1a;单独编写.js文件&#xff…

【HarmonyOS】应用设置屏幕常亮

【HarmonyOS】应用设置屏幕常亮 一、问题背景&#xff1a; 金融类或钱包场景的应用APP&#xff0c;对于付款码&#xff0c;扫一扫等场景都会对屏幕设置常亮。防止屏幕长时间不操作&#xff0c;自动息屏。 目前这种场景的需求也是非常有必要的&#xff0c;也是行业内默认的处理…

Spark 解析嵌套的 JSON 文件

1、什么是嵌套的JSON文件&#xff1f; 嵌套的JSON文件是指文件中包含了嵌套的JSON对象或数组。例如&#xff0c;以下是一个嵌套的JSON文件的示例&#xff1a; {"name": "John","age": 30,"address": {"street": "123…

解码目标检测:可解释性的关键角色

解码目标检测&#xff1a;可解释性的关键角色 在人工智能的浪潮中&#xff0c;目标检测作为计算机视觉领域的一个核心任务&#xff0c;已经取得了显著的进展。然而&#xff0c;随着深度学习模型在这一领域的广泛应用&#xff0c;模型的可解释性逐渐成为研究者关注的焦点。本文…

HarmonyOS实现跨语言交互(Node-API)

Node-API简介 通过Native接口&#xff0c;实现两种代码的交互。 是在Node.js提供的Node-API基础上扩展而来&#xff0c;但与Node.js中的Node-API不完全兼容。本质就是提供了对C/C代码的使用接口&#xff0c;使得两种代码共同工作。规范I/O、CPU密集型、OS底层等能力。 应用场景…

工作中es客户端常见使用错误

背景&#xff1a; 7月9日因阿里云底层网络故障导致使用阿里云产品&#xff08;redis&#xff0c;rocketmq等&#xff09;均受影响&#xff0c;因为业务依赖mq异步将数据写入elasticsearch中&#xff0c;mq发送失败导致es部分数据丢失。丢失的数据需要从mysql中恢复到es中&…

景区AR导航营销系统:技术解决方案与实施效益分析

随着旅游市场的竞争日益激烈&#xff0c;景区需要不断创新以吸引游客。景区 AR 导航将虚拟画面与现实场景相结合&#xff0c;为游客提供了更加直观、生动的导航服务。对于景区而言&#xff0c;这一创新技术无疑是吸引游客目光、提升景区知名度的有力武器。通过独特的 AR 导航体…

CentOS怎么关闭自动锁屏?

禁止自动锁屏 有时候几分钟不用Centos&#xff0c;系统就自动锁屏了&#xff0c;这是一种安全措施&#xff0c;防止别人趁你不在时使用你的系统。但对于大部分人而言&#xff0c;这是没有必要的&#xff0c;尤其是Centos虚拟机&#xff0c;里面没啥重要的东西&#xff0c;每次…

如何将整个运行环境打包成docker

场景 某个项目&#xff0c;用的tomcatrediszookeeper&#xff0c;然后这个项目已经产品化&#xff0c;很多地方都需要部署&#xff0c;并且有很多有细微差别的版本。 然后我这边是需要部署测试环境&#xff0c;一台机可能会部署好几个。 按照传统部署方式&#xff0c;要好几个…

STM32-寄存器ADC配置指南

目录 输入方式&#xff1a; 模拟看门狗功能&#xff1a; ADC中断 配置一个Demo 设置时钟 自校准 通道选择 采样时间选择 转换模式选择 断续模式 启动转换 软件触发 外部触发 转换结束 关于DMA 模拟看门狗 ​编辑ADC数据位置​编辑 在STM32F中&#xff0c;ADC可…

opencv—常用函数学习_“干货“_13

目录 三四、机器学习 支持向量机&#xff08;SVM&#xff09; K近邻算法&#xff08;KNearest&#xff09; 随机森林&#xff08;RTrees&#xff09; 朴素贝叶斯分类器&#xff08;NormalBayesClassifier&#xff09; 自适应增强算法&#xff08;Boost&#xff09; 多层感…

ASP.NET CORE依赖注入全面解析:理解DependencyInjectionAbstractions的核心

引言 ASP.NET Core中&#xff0c;依赖注入&#xff08;Dependency Injection, DI&#xff09;是其核心功能之一。它通过Microsoft.Extensions.DependencyInjection命名空间提供默认的DI容器&#xff0c;实现了松耦合和模块化设计。在本文中&#xff0c;我们将深入剖析ASP.NET …

通过json传递请求参数,如何处理动态参数和接口依赖

嗨&#xff0c;大家好&#xff0c;我是兰若姐姐&#xff0c;今天给大家讲一下如何通过json传递请求参数&#xff0c;如何处理动态参数和接口依赖 1. 使用配置文件和模板 在 test_data.json 中&#xff0c;你可以使用一些占位符或模板变量&#xff0c;然后在运行测试之前&…

手写数字识别(机器学习)

一&#xff1a;一对多分类方法(one-vs-all) 这里先上的代码&#xff0c;想看原理可以到代码下面。 在数据集中&#xff0c;y的取值为1~10&#xff0c;y10表示当前数字为0 首先读取数据,并对数据进行切分。 import pandas as pd import numpy as np import matplotlibmatplo…

【BUG】已解决:IndexError: positional indexers are out-of-bounds

IndexError: positional indexers are out-of-bounds 目录 IndexError: positional indexers are out-of-bounds 【常见模块错误】 【解决方案】 原因分析 解决方法 示例代码 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博…

JavaScript:数组排序(冒泡排序)

目录 一、数组排序 二、sort()方法 1、基本语法 2、默认排序 3、自定义排序 三、冒泡排序 1、基本概念 2、实现步骤 3、过程解析 4、代码示例 5、时间复杂度 一、数组排序 对一个给定数组进行处理&#xff0c;使其从无序变为有序&#xff0c;这个过程就是数组排序&…