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,一经查实,立即删除!

相关文章

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实现跨语言交互(Node-API)

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

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

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

CentOS怎么关闭自动锁屏?

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

STM32-寄存器ADC配置指南

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

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

一&#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;这个过程就是数组排序&…

二叉树基础及实现(二,加经典OJ)

目录&#xff1a; 一 .接引二叉树(一) 二 .二叉树相关oj题&#xff1a; 1. 检查两颗树是否相同 2. 另一颗树的子树 3. 翻转二叉树 4. 判断一颗二叉树是否是平衡二叉树 5. 对称二叉树 6. 二叉树的构建及遍历 7. 二叉树的分层遍历 8. 给定一个二叉树, 找到该树中两个指定节点的最…

Fine-BI学习笔记

官方学习文档&#xff1a;快速入门指南- FineBI帮助文档 FineBI帮助文档 (fanruan.com) 1.零基础入门 1.1 功能简介 完成四个流程&#xff1a;新建分析主题、添加数据、分析数据、分享协作。 示例数据获取&#xff1a;5分钟上手FineBI - FineBI帮助文档 (fanruan.com) 1.2 …

R语言优雅的进行广义可加模型泊松回归分析

泊松回归&#xff08;Poisson regression&#xff09;是以结局变量为计数结果时的一种回归分析。泊松回归在我们的生活中应用非常广泛&#xff0c;例如&#xff1a;1分钟内过马路人数&#xff0c;1天内火车站的旅客流动数&#xff0c;1天内的银行取钱人数&#xff0c;一周内的销…

【已解决】如何使用母版视图统一PPT格式?

母版视图在PPT中是一个强大的工具&#xff0c;可以帮助我们统一幻灯片的格式、布局和设计风格。今天来看看如何利用母版视图统一PPT格式&#xff0c;让每张幻灯片看起来一致和专业。 第一步&#xff1a;打开母版视图 打开PPT后&#xff0c;在顶部菜单栏中&#xff0c;选择【视…

Docker容器限制内存与CPU使用

文章目录 Docker 容器限制内存与 CPU 使用内存限额内存限制命令举例使用 `nginx` 镜像学习内存分配只指定 `-m` 参数的情况CPU 限制命令举例验证资源使用Docker 容器限制内存与 CPU 使用 在生产环境中,为了保证服务器不因某一个软件导致服务器资源耗尽,我们会限制软件的资源…

windows服务器启动apache失败,提示请通过cmd命令行启动:net start apache

Windows Server 2012 R2服务器突然停止运行apche&#xff0c;启动apache失败&#xff0c;提示请通过cmd命令行启动:net start apache 1.报错截图&#xff1a; 进入服务里输入命令启动也不行&#xff0c;提示由于登录失败而无法启动服务。 2.问题原因&#xff1a; 服务器www用…

Node.js知识点总结

Node.js知识点总结 Node.js其本质和浏览器一样是一个JavaScript运行环境。浏览器的运行环境为V8引擎浏览器内置API&#xff08;BOM、DOM、Canvas);而node.js的运行环境是V8引擎node提供的API(fs、path、http)。它使JavaScript可以编写后端。 基本知识 fs文件系统模块 它提供一…

springboot宠物相亲平台-计算机毕业设计源码16285

目 录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2 开发环境及相关技术介绍 2.1 MySQL数据库的介绍 2.2 B/S架构的介绍 2.3 Java语言 2.4 SpringBoot框架 3 宠物相亲平台系统分析 3.1 可行性分析 3.1.1 技术可行性分析 3.1.2 经济…

基于FPGA + Qt + OpenCv的人脸考勤系统

一:界面设计 客户端界面设计: 服务端界面设计: 简介:首先服务端在注册界面先注册人脸,然后客户端界面进行人脸识别,将人脸识别的图像发送给服务端以后,服务端在图像数据库里寻找人脸比对,若有数据就将查询到的个人信息发送给客户端,并在客户端显示,查询界面是用来查…