vector模拟实现
大家好,接下来我将带来有关vector的模拟实现。
跟着博主的步伐,咋们一定可以实现出自己的vector的。
1.vector实现接口概略
operator=、operator[]、
begin、end、
size、resize、capacity、empty、reserve、
front、back、push_back、pop_back、insert、erase、
swap、
如上实现接口都是使用比较多的,如果有没的接口的话,可能是使用的比较少,博主就没有实现,
2.大体框架
我们的vector是通过维护一个连续的空间来实现的
我们的_start指向空间中第一个元素,_end指向有效空间的最后一个位置的后一个位置,_endofstorage指向最多可容纳空间的最后一个位置的下一个位置
2.具体实现1
2.1.size函数
这个函数就比较简单,我们使用_finish的指针减去_start指针即可
2.2.capacity函数
这个函数同上一个也是类似的,使用_endofstorage减去_start指针即可了
2.3.reserve函数
这个是我们的扩容函数,我们要将空间扩容到n的大小去。
如果出现原来空间都比扩容的n大的话我们就不用去管了
当然当我们扩容的时候,我们首先就是要new T[n]
创建n个对应大小的空间,将返回的空间指针给tmp
如果_start不为空,代表原来空间有数据,那么我们现在就是要将原空间的内容拷贝到新空间去了。
拷贝的时候我们先记录下数据的个数sz,然后再拷贝。
拷贝结束了我们就要将原来的空间给delete释放掉才行,不然会导致内存的泄漏
之后再让维护的_start指向对应的tmp
我们的_finish和_endofstorage指针也得跟着修改,指向正确的空间
2.4.resize函数
我们需要resize函数去将vector中的数据size个数进行修改,并且赋值
我们函数是要将原有的size更新为n
如果n小于原size我们就直接将_finish的指向位置修改即可
但是如果n大于原size的话我们就需要给多出来的那几个空间赋值成初始值val
这个val后面给的缺省值是T()
我们知道的是如果T是一个自定义类型,那么T()就是对应的一个匿名对象了。比如当T是一个string的时候,那么我们就调用string的一个默认构造函数初始化了一个匿名对象。
但是如果T是一个内置类型呢?内置类型有默认构造函数?
c++在这里有对内置类型进行升级,它们有自己的构造函数
int j = int();就不会报错 int j = int(1);也没错
所以我们这样写T()就没有问题,而且也使得内置类型和自定义类型都可以自由调用这个了
我们继续看一下这个赋值地方,我们要注意的是,我们要重新赋值的是多出来的那部分,所以我们就要从_finish指向的位置开始进行赋值成val
3.具体实现2
3.1.begin函数、end函数
我们的begin函数和end函数我们只要返回我们的_start和_finish指针即可较简单
3.2insert函数
我们需要在外面的vector里面插入一个值
我们来看一下这个扩容可以吗?
其实是不可以的!
为什么呢?
因为这样扩容看似可以,但是我们reserve后,_start和_finish这些都指向其他位置去了,pos还如何去后面执行插入呢?
所以这样的扩容逻辑是很有问题的。
我们这样记录以下pos到_start的距离长度,然后就可以通过新的_start找到pos了
但是大家注意我这次给pos加上引用了。不然我们修改了pos位置,但是外面的pos还是错误的
但是?这样是对的吗?
其实并不是,因为如果我们使用v.insert(v.begin(), 3);就有问题了,因为v.begin()是通过传值返回的,肯定不能在这里使用引用去接收啊!!
所以我们以后去使用insert函数这里就得要小心有迭代器失效了
这样我们便将这个插入函数给写好了,大家一定要注意,最后要返回pos这个指针,不然发生迭代器失效了,怎么找pos这个位置呢?
3.3.push_back函数
push_back函数现在就比较简单了,我们前面实现了insert函数,我们将它附用一下即可
3.4构造函数
我们的构造函数有三个我们常用到的
1.默认构造函数 --- 1
这个函数我们只要跟vector库里面实现的一样就行了,我们只要一来在初始化列表初始化为nullptr即可。
2.构造函数 --- 2
我们是通过这个构造函数直接创造出n个空间,全部放val
我们有前面实现的resize函数,这个还是比较容易的,我们去附用一下resize函数
3.构造函数 --- 3
这个构造函数就不一样了,我们需要通过一个区间去初始化
但是我们要去解决这个,我们用什么去接收这个指针呢?
我们这时候就需要再加入一个模版了
但是我们这里使用模版后我们使用的时候会发现出现一个情况:
zcy::vector<int> v(10, 2)通过不了?
为什么呢?
其实原因很简单,因为我们的10,2被优先传到了这个模版里面了
为什么呢,因为我们实现的2的n是size_t类型的,比起这个模版来说只能是较匹配。
所以他先到了这个模版里面去了。
我们就得再实现一个2:
这个重载一个int类型的n,这样就避免误传入到模版那里面去了
3.5.析构函数
析构函数这里比较简单,我们只需要将对应的_start给释放掉即可了
4.具体实现3
4.1.swap函数
swap函数是交换两个vector对象的
4.3.拷贝构造函数
我们要注意的是赋值这个地方,不能去使用memcpy这个函数了,这个函数遇到自定义类型也是直接浅拷贝过去的就导致会出现问题。
我们需要这样一个一个的去赋值,调用自定义类型重载的赋值匀速符重载
4.3.operator=运算符重载函数
我们是通过一个对象来对另一个对象赋值,修改它
在这里我使用了现代的写法,我们之前传值进入,然后通过拷贝构造函数拷贝一份到tmp里面去
,我们通过我们实现的交换函数在将tmp和自己进行交换,tmp就保存自己的那份内容
出了函数生命就结束了,去执行析构了,就简介把空间析构了
4.4.empty函数
这个函数比较简单,直接判断里面size是不是0即可了
4.5operator[]运算符重载函数
我们要注意的就是需要引用返回了,因为这个空间还在是支持我们进行修改的
再实现一个const版本的,就支持const对象使用了
4.6.front函数和back函数
这两个函数也是很简单的,我们返回对应的头尾位置的值即可
4.7.erase函数
这个函数我们实现发现不会出现迭代器失效的问题,但是我们还是将它当做是失效了。和insert统一起来,而且我们的vs中也有强制检查这个。
这个地方,我们判断一下pos位置的正确性后,就是移动数据了,这个部分问题不大。
大家可以看看。
4.8.pop_back函数
我们这个有了erase后,也变得很简单,我们只用去附用一下它即可了
5.结语
大家看过后应该也可以实现出自己的vector了,大家多多点赞收藏,谢谢!
我后期会继续更新的,如果有不懂的话可以先去看一下前面写的string模拟实现
希望大家有所收获!