作者:几冬雪来
时间:2023年9月15日
内容:C++——vector知识讲解
目录
前言:
在前一篇博客中我们初步的了解了C++的知识,并且通过和string的对比,我们也是发现了二者的相似之处。因此在书写vector的时候,成本要比string的时候小一些。而今天我们将继续来接收vector。
vector<string>:
在第一篇博客中我们就有说到,在写代码的时候可以将string嵌套在vector当中,这种方式的结果也就是大家熟知的二维数组。
那么vector<string>是如何实现的呢,它又是什么样子的?在这里画一张图来了解它。
构造函数:
同样的vector也可以进行构造函数。
那么如果要在头文件中对其进行构造,我们要如何对其进行书写,它的书写形式和string的时候一样吗?
为了探讨这个问题,我们拿来了一份库中的代码来观察。
这就是库中vector中push_back和reserve的构造函数的写法。
这个有人就要说了,库中的好多英文词都没懂,比如allocate_and_copy,destroy等等。但是即使不知道这些词的意思,我们也可以依靠代码去推断出它在此处的作用。
那为什么要去看库里面的代码呢?
这是因为以后去实习的时候,我们是不可能一开始就对一个项目从头负责到尾的,因此在这之前有不少人都写过代码,这个时候就要去推测哪段代码是什么意思了。
在进行了逐步解析之后,我们可以得到这一张图片。
那么依靠这一张图片中的逻辑和方式,我们就可以来书写自己的代码。
因为头文件要一次将代码全部输入并且讲解完毕难度过大,因此在讲解哪部分哪部分代码的时候我们会将其分开说明。
创建模板:
一开始既然要构建函数,那么模板就是必不可少的东西。
因为string和vector有相似之处,因此二者的模板的写法也是及其的相似,这里就不过多的进行讲解了。
至于下面的_start,_finish和_endofstorage则是跟我们库中的保存一致。作用在上面有对其进行说明。
reserve/push_back:
接下来就是我们插入数据和扩容的操作了,这两个英文相信大家也不会太陌生。
先来讲解reserve,一开始还是老规矩判断是否需要进行扩容操作。
根据库中的代码,开头我们需要创建一个sz来保存我们旧数据的size,这是因为在后面赋值给_finish的时候,如果不保存的话原先的数据会被修改。
下来就是走流程,开辟新空间。判断然后对其进行拷贝,最后删除原空间的数据再对新空间进行标记。
然后就是我们的插入值的代码。
首先还是判断空间是否已经是满了的状态,如果满了的话这个地方就需要判断扩容的大小。要是一开始capacity为0的话,这里就开4个大小的空间,否则就开原空间的两倍大小。
然后用reserve来进行扩容。最后在将_finsih进行赋值就行了。
迭代器/capacity和size:
再下来就是我们的迭代器了,这里的begin和end就不需要多讲了。
要注意的一个点是要将_start这些都nullptr成空。
同样的size和capacity就只需要计算出来它们的返回值就行了。
写到这里大家再去看我们头文件里面的代码, _start, _finish,_endofstorage。这三个名词我们极少用到,看起来还不如当初书写string的时候。
但是这并不是就代表它们可以被忽略,接下来我们就再写一个接口来看看。
insert:
同样的在string中insert也有出场过,它代表的意思是在某个位置插入一个值。
那么如果要将其书写在头文件中是怎么样的形式呢?
这里我们来写写看。
首先pos是要选择插入的位置,x是想要插入的值。
在插入数据之前要先对其位置进行判断,插入的位置需要在空间当中,不然会出问题。
在判断的时候我们加入了“<=”和“>=”并不是单纯的“<”和“>”,这是因为insert代码也可以用做头插和尾插。
然后是判断是否需要进行扩容操作。
再接下来就需要对pos位置后的数组进行进行一个后移的操作,让pos处插入x的时候不用覆盖原数据。
最后再将x值交给pos位置,并且因为插入了一个值,因此_finish也要往后增加一格。
到这里我们的insert代码就完成了一大半了。
为什么说的一大半呢,知识因为在这其中还有隐藏的问题所在。
刚刚就有说过,insert可以运用于头插操作。
但是当满足一定条件的时候,这个时候我们会插入失败。
那个问题被我们叫为迭代器失效,在我们插入数据的时候(插入4/8个后进行扩容)。因为要扩容,原位置的数据要进行删除,这里在原数据中的pos也会被删除。
这就是导致插入的数据可能变成随机数。
改进:
要对代码进行改进的话,就需要我们保存pos,最后再把pos给数组就行。
拓展:
这里我们就解决了代码内部的迭代器失效的问题。
这里提到了内部,那是因为还存在有外部的迭代器失效。
像这个样子就可能会导致外部的迭代器失效。
在头文件中,pos并不是被const过的(头文件const后还修改很多东西),因此它是传值返回,这里数据的改变并不会影响到最后的结果。
这里要记住一个点,insert以后迭代器看看你会失效(扩容条件不同),因此insert以后就不要使用这个形参迭代器了,因为他可能已经失效了。
结尾:
到这里我们的又一篇vector的讲解结束了,也是快到我们的vector结束的篇章了。下一篇博客可能就会将vector完结,并且也会讲解vector中从的一些问题,最后希望这篇博客可以帮到各位。