vector的功能讲解与底层实现

本文主要介绍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.mzph.cn/news/845432.shtml

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

相关文章

dnsrecon一键开始负载平衡检测(KALI工具系列十四)

目录 1、KALI LINUX简介 2、lbd工具简介 3、在KALI中使用lbd 3.1 测试目标域名是否存在负载不平衡 4、总结 1、KALI LINUX简介 Kali Linux 是一个功能强大、多才多艺的 Linux 发行版&#xff0c;广泛用于网络安全社区。它具有全面的预安装工具和功能集&#xff0c;使其成为…

TCP协议详解及其相关的10个核心机制(面试重点)

TCP协议的报文格式 TCP协议有连接&#xff0c;可靠性传输&#xff0c;面向字节流&#xff0c;全双工。 他的数据格式如图&#xff1a; 根据他的数据格式&#xff0c;在这里我们只知道 16位源端口号&#xff08;表示客户端这里的端口号&#xff09;&#xff0c;16位目的端口号&…

算法简单笔记4

5月31号&#xff0c;明天决赛&#xff0c;今天脑子也是一滩浆糊&#xff0c;踏马的一道题也做不出来&#xff0c;超级难受&#xff0c;只好简单复盘一下两道之前的题目&#xff0c;看完就差不多了&#xff0c;再学也没啥用了&#xff0c;写完这两题题解我就回去打把steam绝地求…

深度学习聚类再升级!新算法实现强悍性能,准确率超98%

深度聚类不仅继承了传统聚类算法的优点&#xff0c;在对高维和非线性数据的处理能力&#xff0c;以及自适应性和抗噪性方面也具有很大优势。 具体来说&#xff0c;结合深度学习的聚类算法通过利用深度神经网络的强大特征提取能力&#xff0c;自动学习和识别数据中的复杂结构和…

全志H616(BIGTREETECH CB1)和 博通BCM2711(树莓派4B)CPU对比测试

一&#xff0c;实物对比图&#xff1a; BIGTREETECH CB1的底板接口的分布和树莓派4B是一样的&#xff0c;但是没有树莓派的音频接口&#xff0c;底板也不能放到树莓派4B的官方外壳里&#xff0c;因为底板的背面有一个DSI接口&#xff0c;高度超出了。 二&#xff0c;开发板硬…

HBSL-22Q/K定时限过电流继电器 板前接线 JOSEF约瑟

HBSL系列静态定时限过电流继电器 系列型号&#xff1a; HBSL-11A/E静态定时限过电流继电器&#xff1b;HBSL-11A/K静态定时限过电流继电器&#xff1b;HBSL-12A/E静态定时限过电流继电器&#xff1b; HBSL-12A/K静态定时限过电流继电器&#xff1b;HBSL-21A/E静态定时限过电…

JS-09-es6常用知识1

目录 1 模板字符串 1.1 模板字符串基本用法 1.2 模板字符串解决了一些痛点 2 解构赋值 2.1 对象的解构赋值 2.2 函数参数的解构赋值 2.3 补写&#xff1a;属性的简写 3 rest参数 3.1 arguments 3.2 rest参数 3.3 补充&#xff1a;判断数据类型 4 箭头函数 4.1 …

SpringBoot-世界杯足球赛网站-28567

Springboot世界杯足球赛网站 摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对世界杯足球赛…

jsmug:一个针对JSON Smuggling技术的测试PoC环境

关于jsmug jsmug是一个代码简单但功能强大的JSON Smuggling技术环境PoC&#xff0c;该工具可以帮助广大研究人员深入学习和理解JSON Smuggling技术&#xff0c;并辅助提升Web应用程序的安全性。 背景内容 JSON Smuggling技术可以利用目标JSON文档中一些“不重要”的字节数据实…

C++ 特殊运算符

一 赋值运算符 二 等号作用 三 优先级和结合顺序 四 左值和右值 五 字节数运算符 条件运算符 使用条件运算符注意 逗号运算符 优先级和结合顺序 总结

实战15:bert 命名实体识别、地址解析、人名电话地址抽取系统-完整代码数据

直接看项目视频演示: bert 命名实体识别、关系抽取、人物抽取、地址解析、人名电话地址提取系统-完整代码数据_哔哩哔哩_bilibili 项目演示: 代码: import re from transformers import BertTokenizer, BertForTokenClassification, pipeline import os import torch im…

【linux】docker下nextcloud安装人脸识别插件2

接上文 【linux】docker下nextcloud安装人脸识别插件-CSDN博客 由于作者不再维护此插件&#xff0c;转而开发新的插件 recognize &#xff0c;因此同步更新插件使用教程。 1、下载人脸识别app&#xff1a;recognize Recognize - Apps - App Store - Nextcloud 2、将插件recog…

Java解析JSON并修改属性值:从JSON到JsonObject的逐层解析

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 在Java中&#xff0c;可以使用各种库来解析和操作JSON数据。其中&#xff0c;Gson和Jackson是两个非常流行且功能强大的库。在这篇文章中&#xff0c;将使用Gson库来解析给定的JSON字符串&#xff0c;修改operationB…

python移位操作符(左移位操作符<<、右移位操作符>>)(允许开发者对整数进行位操作,乘2或除2)(左移操作、右移操作)(位掩码操作|=)

文章目录 Python 中的移位操作符详解移位操作符简介左移位操作符 (<<)语法和使用示例代码输出 右移位操作符 (>>)语法和使用示例代码输出 移位操作符的应用场景快速乘除运算&#xff1a;使用移位操作符代替传统的乘法和除法运算&#xff0c;可以提高计算速度。位掩…

生产问题(十六)数据丢失-mysql binlog排查

一、引言 最近作者遇到一个线上问题&#xff0c;是mysql 的数据丢失了&#xff0c;这里记录一下排查过程和binlog的分析。 二、问题 问题出现的表象是应用系统大量报错&#xff0c;各种空指针之类的&#xff0c;这种一看就不可能是代码发布的问题&#xff0c;原因一定在框架、…

第十八节:认识一些经典递归过程

一 暴力递归就是尝试 1&#xff0c;把问题转化为规模缩小了的同类问题的子问题 2&#xff0c;有明确的不需要继续进行递归的条件(base case) 3&#xff0c;有当得到了子问题的结果之后的决策过程 4&#xff0c;不记录每一个子问题的解 二 打印n层汉诺塔从最左边移动到最右边的全…

分形之科赫雪花

前言 分形是一种具有自相似性的几何图形或数学对象。它的特点是无论在任何放大或缩小的尺度下,都能够看到与整体相似的图形。分形的形状可以非常复杂,常常具有分支、重复的图案,以及细节层次丰富的结构。 分形在自然界中广泛存在,如云朵、树枝、山脉、海岸线等,它们都展…

Java常用API(三)

一、Arrays类 1.定义 Arrays是一个用于操作数组的工具类。 2.常用方法 1.toString方法 public class Demo {public static void main(String[] args) {//toString 将数组变成字符串int[] arr {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};System.out.println(Arrays.toString(arr));…

故障——pod状态为pending该如何解决

一、报错问题 当您的虚拟机出现如下报错&#xff0c;您首先快照一下虚拟机如果仍然报错 二、解决方法 2.1 任务管理器 首先进入任务管理器&#xff08;CtrlAltDel&#xff09;内&#xff0c;查看所有vmware是否都处在运行的状态&#xff0c;可能会跳出一个比较突兀的进程&#…

【Spring Cloud】微服务链路跟踪Sleuth

目录 为什么要使用微服务链路跟踪微服务的现状多服务协同工作复杂的调用链条容易出错 微服务链路跟踪需要实现的需求实现监控决策避免技术债务快速定位故障 微服务链路跟踪的技术要求低消耗应用透明延展性可控采样率可视化 Spring Cloud Sleuth简介Spring Cloud Sleuth的4个特点…