vector的功能讲解与底层实现

news2024/11/18 15:27:38

本文主要介绍vector的内容以及使用和模拟实现。

vector在英文翻译中是矢量的意思,但在c++中他的本质是一个顺序表(容器),是一个类模板,(用模板创建变量就要参考我们之前的实例化内容了)用可以改变其size的数组去实现,有了之前string的讲解,这部分的推进会顺利很多,与之前的内容分块大致相同。

从使用上来讲,vector和string的使用几乎是一模一样的,只不过string内部存放的是字符串,而vector内部可以存放任何类型,比如int char甚至是string以及vector(即vector<string>,vector<vector<int>>),而vector的各常用的函数,比如push_back,size,resize等语法与string都是相同的,我们在此就不作详细讲解了。我们在此介绍一下vector有的且string类未提到的一些知识。

std中的sort

在使用前,我们需要包含算法的头文件, algorithm。sort的用法是参数需要传迭代器(相比我们之前c中的qsort的参数,这个函数传参就简便了很多)

接下来我们实现一下vector的底层

他的结构我们可以类比顺序表,只是指向数组的指针的类型我们用模板代替

但我们查看vector的源码后发现其底层其实是使用了迭代器,因此,我们采用迭代器版本来实现底层

第一个相当于T*a,第二个相当于a+size(也是指针,指向最后一个元素的后一个的位置,相当于string的'\0'),第三个相当于capacity。接下来我们就实现它的常用函数

push_back

与之前的string一样,我们在插入之前要判断是否还有空间,如果不够就进行扩容

我们来看看扩容的具体操作,与之前的string类不同的是,我们的vector没有size、capacity的概念,所以我们需要写一个函数来实现,

这样我们就能得到我们新空间的容量大小,那么size的函数也是同理

我们重点看看这个reserve函数的实现,根据之前的string的reserve的思路,大概就是用new开空间然后memcpy拷贝然后释放旧空间然后改变指向。于是我们就可以实现以下代码。

但当我们想具体插入一些数并运行一次时发现报错了,问题就出在这个reserve函数的倒数第二行代码上。

上面是我们扩容后的新空间,但是当走到倒数第二行执行size()函数时,此时是下面的finish减去上面的start(因为start已经在上面扩容的过程被修改了)并不能得到我们想要的实际size,为了解决这个问题,我们可以进行以下修改

但是这就影响了代码的可读性,导致一个新接手的人可能不知道这里为什么是这样写,因此,我们只能另辟蹊径。其实发现,上面的方法无非就是先把保留的start给了finish然后再修改start。所以我们可以创建一个变量把修改之前的内容保存一下再用即可。

接下来我们实现一下析构函数

只需要释放原来开辟的空间以及改变指针指向即可。有了以上的各个函数以及运算符重载,我们就可以实现数组形式的[]遍历数组了,但此时用范围for是不可以的,因为范围for依赖迭代器而我们还没有具体实现底层,所以我们来实现一下迭代器然后实现一下两种形式的遍历。而迭代器的实现就简单的多了。

有了迭代器,我们就可以实现范围for的遍历了。先创建一个vector变量然后进行数据插入最后两种遍历并打印,代码如下:

当然,迭代器还有以下方法实现遍历

第一行的类型名就等同于int*it,我们上面这么写是因为iterator是定义在类域里面的,如果直接写成iterator it编译器会找不到。而我们这么写因为iterator不一定是指针,这种写法是通用的方式,而用指针替代有时候会不通过,只是我们当前可以理解为它和指针是等价的。当然,vector中的迭代器也有const版本。

由于尾删的函数没啥难度,所以我们就直接一带而过了

接下来的重点是insert和erase函数的用法,这里的参数与之前的不同,采用了迭代器,我们先展示一下它的用法

通过运行结果我们也能大致看出他们的用法了,insert在指定迭代器位置前插入数据,erase删除指定迭代器位置的元素。当然,我们如果想在指定位置插入或删除只需要在begin()后+数字就能实现了,但如果我们想在指定元素之前插入元素呢?首先我们需要在数组中找到该元素所在的位置,所以我们就要用到find函数,遗憾的是,vector内部并没有提供find函数,而是把这个函数写在了算法中,以用于复用(毕竟find函数在大部分类中还是很常用的,而string单独提供了find是因为我们无法用算法中的fing去寻找字符串中的子串)。

复用中的find函数用法如下:需传三个参数,开始查找的位置,结束的位置,需要寻找的值。如果真的找到了,就会返回目标值的位置,下面我们来实现一下在指定位置前插入指定数值。

以上就是在5的位置之前插入666.

但具体的insert和erase函数底层我们还没有实现,搞一下。思路和之前的类似,挪动之后的数据然后插入即可。

但我们真正运行的时候,发现其插入的是随机值,这里的错误涉及到一个新的领域——迭代器失效

原因是:在扩容的时候,虽然start和finish在扩容后会改变为新扩容空间的start和finish,但pos的迭代器在扩容后仍指向该位置,但这块空间已经被释放了,造成了野指针的行为。所以我们需要对pos也进行“迁移”。

但同时我们要注意这里修改的pos是形参,对实际insert的迭代器并没有修改,所以实参的迭代器在扩容操作后仍是失效的,我们就不能进行访问了,为了解决这个问题,我们统一不访问(因为你也不知道是否会扩容,扩容就变成了野指针)。  insert函数作者没有加assert,可以判断一下是否越界。

insert搞完了,看看erase

与insert同样的移动思路。但这里也存在迭代器失效的问题,在我们删除数据以后,it逐渐向后移移到最后一个元素的下一个位置,虽然其不是野指针,但我们最好不要去访问,而且,删除数据有时候还涉及到缩容的问题,那么又要开空间拷贝数据,这样it就是野指针了,更是不可访问,在前一种情况中,不同环境下的情况不同,vs下则会直接报错,而linux不会,为了统一,我们就不访问该迭代器了。但stl库中的vector也给了解决方案,库中的vector具有返回值,其返回的是删除位置的下一个位置的迭代器。

接下来我们实现一下拷贝构造函数,思路也很容易,只需要利用范围for把每个数据都进行插入操作即可。

这里我们提前进行了开空间的操作,提高了pushback函数的效率。但当我们进行拷贝实例操作时却不通过,原因是没有找到构造函数,在类与对象我们讲过,拷贝构造函数也属于构造函数,如果我们不写编译器自己生成,但现在我们已经写了拷贝构造,编译器就不会生成构造函数。在我们新创建一个vector变量时找不到其构造函数了,为了解决这一问题,我们提前剧透一下:

这行语句的作用就是强制编译器生成默认构造函数。拷贝都搞了,再看看赋值运算符重载:

这个更简单,我们利用vector中的swap函数将临时拷贝交换到我们的目标变量即可,同时我们还要把swap函数完善一下。

我们来看下一个问题,假设我们现在要在实现一个string类型的vector并插入字符串

结果运行后却是随机值,问题并不在这里,而是我们之前的扩容的函数,

memcpy本质是做到了浅拷贝,也就是说,在完成扩容后,原来的空间被delete释放掉,而新的tmp仍指向该空间,就是随机值,所以,我们采用逐一赋值的方法。

以上就是我们对vector的内容剖析以及底层实现,对小伙伴们有帮助的话麻烦点个赞再走!

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

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

相关文章

云硬盘的基准性能测试场景

参考来源&#xff1a; 深入浅出云计算-05 | 云硬盘&#xff1a;云上IO到底给不给力 云硬盘的性能等级 当下的云硬盘经过了多次的软硬件迭代&#xff0c;尤其是SSD的迅速发展&#xff0c;吞吐量和随机读写能力等各项性能指标都已经不再是问题了。在现代云计算中&#xff0c;已…

解析前端开发中同源策略与配置代理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 在前端开发中&#xff0c;跨域请求是一个常见的问题。同源策略限制了浏览器中一个页面…

2.1Docker安装MySQL8.0

2.1 Docker安装MySQL8.0 1.拉取MySQL docker pull mysql:latest如&#xff1a;拉取MySQL8.0.33版本 docker pull mysql:8.0.332. 启动镜像 docker run -p 3307:3306 --name mysql8 -e MYSQL_ROOT_PASSWORDHgh75667% -d mysql:8.0.33-p 3307:3306 把mysql默认的3306端口映射…

字符串-至多包含K种字符的子串中最长子串(mid)

一、题目描述 二、解题思路 借鉴以下题目思想&#xff0c;使用双指针&#xff0c;外层循环右侧指针移动&#xff0c;内存循环左侧指针移动 字符串-最长不含重复字符的子字符串(mid)-CSDN博客文章浏览阅读622次&#xff0c;点赞17次&#xff0c;收藏4次。java刷题&#xff1a;…

芯盾时代API安全监测平台

面对难搞的API安全&#xff0c;芯盾时代作为领先的零信任业务安全产品方案提供商&#xff0c;给出了自己的答案——以AI技术赋能API安全&#xff0c;打造API安全监测平台&#xff0c;帮助企业建立资产摸得清、漏洞看得透、攻击测得出、数据拦得住的API风险监测体系&#xff0c;…

通过LabVIEW提升生产设备自动化水平

现代制造业对生产设备的自动化水平提出了越来越高的要求。使用LabVIEW这一强大的图形化编程环境&#xff0c;可以显著提升生产设备的自动化程度&#xff0c;改善生产效率和产品质量。本文将详细分析如何通过LabVIEW改善生产设备的自动化水平&#xff0c;并提供具体的实施策略与…

c++函数基础总结

在给出的代码片段中&#xff0c;我们看到两部分内容&#xff1a;一个类定义和一个全局函数声明。让我们逐一分析它们&#xff1a; 类定义&#xff1a; cpp复制代码 class { public: void a(); }; 这个类定义是不完整的&#xff0c;因为它没有类名。但为了说明&#xff0c;我…

【SQL学习进阶】从入门到高级应用【三范式】

文章目录 什么是数据库设计三范式三范式一对多怎么设计多对多怎么设计一对一怎么设计最终的设计 &#x1f308;你好呀&#xff01;我是 山顶风景独好 &#x1f495;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01; &#x1f495;希望您在这里可以感受到一份…

【SpringBoot】SpringBoot整合JWT

目录 先说token单点登录&#xff08;SSO&#xff09;简介原理单点登录的优势单点登录流程分布式单点登录方式方式一&#xff1a;session广播机制实现方式二&#xff1a;使用cookieredis实现。方式三&#xff1a;token认证 JWT数字签名JWT的作用JWT和传统Session1、无状态&#…

精雕细琢,B 端 UI 设计展典雅风范

精雕细琢&#xff0c;B 端 UI 设计展典雅风范

virtualbox中ubuntu22.04网络配置

第一&#xff1a;添加两个网卡&#xff0c;网卡1是NAT方式&#xff0c;网卡2是仅主机模式&#xff08;两个顺序不能颠倒&#xff09; 第二步&#xff1a;启动ifconfig查看网络

LabVIEW车体静强度试验台测控系统

LabVIEW车体静强度试验台测控系统 开发了一种基于LabVIEW的车体静强度试验台测控系统&#xff0c;通过自动化技术提高试验的精度和效率。系统采用LabVIEW软件与S7-200 SMART PLC硬件平台相结合&#xff0c;实现了对液压缸作用力的精确控制和试验数据的实时采集及管理。 传统的…

11.1 排序算法

目录 11.1 排序算法 11.1.1 评价维度 11.1.2 理想排序算法 11.1 排序算法 排序算法&#xff08;sorting algorithm&#xff09;用于对一组数据按照特定顺序进行排列。排序算法有着广泛的应用&#xff0c;因为有序数据通常能够被更高效地查找、分析和处理。 如图 1…

MySQL事务与MVCC

文章目录 事务和事务的隔离级别1.为什么需要事务2.事务特性1_原子性&#xff08;atomicity&#xff09;2_一致性&#xff08;consistency&#xff09;3_持久性&#xff08;durability&#xff09;4_隔离性&#xff08;isolation&#xff09; 3.事务并发引发的问题1_脏读2_不可重…

香橙派 AI pro:AI 加速初体验

香橙派 AI pro&#xff1a;AI 加速初体验 在AI领域&#xff0c;不断涌现的硬件产品为开发者提供了前所未有的便利和可能性。今天&#xff0c;我要介绍的这款产品——香橙派 AIpro&#xff0c;就是其中的佼佼者。在昇腾 AI 芯片的加持下&#xff0c;这款开发板有着出色的算力。…

【WEEK14】 【DAY3】Swagger第一部分【中文版】

2024.5.29 Wednesday 目录 16.Swagger16.1.Swagger简介16.1.1.前后端分离16.1.2.前后端分离时代16.1.3.产生的问题16.1.4.解决方案16.1.5.Swagger 16.2.SpringBoot集成Swagger16.2.1.新建swagger-demo项目16.2.2.导入依赖16.2.2.1.springfox-swagger216.2.2.2.springfox-swagge…

CTFHUB-信息泄露-备份文件下载

目录 网站源码 bak文件 vim缓存 .DS_Store 当开发人员在线上环境中对源代码进行了备份操作&#xff0c;并且将备份文件放在了 web 目录下&#xff0c;就会引起网站源码泄露。 需要用到的工具&#xff1a; dirsearch (目录扫描工具在python3环境下) 网站源码 可以利用bur…

串口屏变量图标显示电量

1、首先制作好电量的图标图片&#xff0c;如下图&#xff1a; 2、然后可以使用美图秀秀逐个修改图片的像素为一致&#xff0c;比如像素为55*32&#xff0c;修改后如下 3、然后打开DGUS_V7.647软件&#xff0c;点击ICL生成工具 4、导入图片&#xff0c;点击生成ICL&#xff0c;如…

2024年中国CRM行业发展方向和前景 | 《连接型CRM》文章精选

01、创新突破&#xff0c;技术为本 中国经济发展处于增速换挡期&#xff0c;企业数字化需求旺盛&#xff0c;同时云计算、大数据、物联网、区块链、5G等新技术的发展&#xff0c;为CRM系统的应用与发展提供了更多的机遇和可能。 近些年来&#xff0c;技术的发展对CRM的重要性…

LabVIEW远程开发与调试

在现代项目开发中&#xff0c;远程开发与调试已经成为一种常见的模式&#xff0c;特别是在使用LabVIEW进行工程项目时。本文将详细分析LabVIEW远程开发与调试的优缺点&#xff0c;并从多个角度说明如何建议客户采用这种方式&#xff0c;以提高项目效率和质量。 优点 灵活性和便…