vector

1.vector的介绍及使用

    vector是用一个可以改变它的size的数组实现的一个顺序容器(顺序表),行为和数组一样,只是它可以动态增长(存数据时可扩容)。vector有构造析构部分,容量,元素访问,修改部分。比如不看文档结合以前学string的经验,现在用之前的三种方式写个vector再遍历一下:

所以基本用法和以前一样,唯一注意的是这是个类模板,要显示实例化,如vector<int> v。那能不能用vector<char> strV代替string str?反正都是字符数组都可以动态增长。不可以,首先string和vector<char>在结构上有所不同,string要求后面有\0,这里为了兼容c接口是自动处理的,vector<char>没有要求,不自动处理。其次string的接口是比vector更丰富的,因为string是专门针对字符的,有+=、比较大小等。string比较大小很正常,vector虽然也可以但没有意义,因为vector要顾虑所有的类型,因此它们的存在都是有各自价值的。vector里面也是可以存string的,模板只要是确定类型都可以模板化,此时可以插入各种string。(先提一下)比如我们插入名字正常这样写:

上图是构造一个对象再push_back,但是太麻烦。第二种方式是用匿名对象:

第三种很多地方都喜欢用:

原本被实例化为string应该传string,这里是单参数构造函数支持隐式类型转化。(后面还会见到vector<vector<int>>,vector里面存vector,这个后面说)。

    下面把vector的整体功能顺一下:

上图这是vector的原型,它在这声明有两个模板参数,一个是T,也就是把里面存的数据类型进行实例化;另一个模板参数就是关于alloc,这是一个空间配置器,也就是内存池,STL所有容器都用了这个内存池,因为要频繁申请空间,为了提高效率弄了个自己的内存池。这里给了缺省参数,默认用它自己的内存池,这给参数是为了有人想用自己写的,所以可以替代。日常中不用管这里,因为默认写的就很好了。第一个就是构造部分:

(1)是无参的构造,(2)是n个val的构造,像value_type通常是typedef的,其实就是T:

(3)是迭代器区间初始化,(4)是拷贝构造。这里简单演示一下:

上图是开个数组初始化为10个1,vector<int> v1(10, 1);开个数组初始化为10个"***",vector<string> v2(10, "***")。( (3)这用的InputIterator后面说,而且发现用的模板,意味着不一定是vector的迭代器,可以给其它类型的迭代器,只要这个迭代器可以像指针一样按迭代器规范就行)可用自己类型或其它类型迭代器初始化:

在看个神奇的,还可以这样初始化:

所以可以用自己类型的迭代器,可以用其它容器的迭代器,还可以用指针区间(类似string底层是指针)。说到迭代区间,这先看个有意思的地方:排序。之前说过STL中有部分叫容器和算法,这是分开的,容器是存数据的,算法是对数据进行处理的。那容器和算法是如何关联的呢?下面来简单看看:算法通过迭代器来控制容器里的数据,所以算法头文件下C++提供了两个sort,用迭代器去访问

这也是模板,意味着不仅仅可作用在一个容器上,各种容器只要符合要求都可以作用。所以对vector排序就很简易:

这个默认排的是升序,如果排降序就多给了个模板参数Compare comp,这个可以自己写,但更多是库里面实现的。库里面实现的有两个,默认升序用的小于,降序用的大于,不是说用符号,而是传一个类对象过去。库中有个叫greater<>,还有个是less(默认用less),greater也是模板,定义greater的对象传过去就可以排了(先了解使用就行):

上述也可以没必要定义有名对象,直接用匿名对象:

其它容器也可以玩,也可以对string对象sort一下,还可以对数组sort一下:

所以模板是很强的。下一部分来看看迭代器:

普通对象用普通正向迭代器,const对象用const正向迭代器,一个是可以读可以写的迭代器,一个是只读的迭代器;还有两种是反向迭代器,一个是可以读可以写的迭代器,一个是只读的迭代器,也是一样的。但vector除了和算法配合外,直接访问还是不喜欢用迭代器的,试一试给排序反向迭代器:

反向排个升序就是降序。再看下一部分:size不用多说,max_size不同平台下不同没参考意义;resize和reserve是有意义的,比如要插入10个数据这样写对不对?

运行时崩了,因为v1.reserve(10)后的size是0,capacity是10。用v1.operator[](i)时里面一上来会assert(i < _szie)检查i是不是小于size,此时size是0所以崩了。所以想这样做就应该用resize:

resize把空间开好的同时也就把size涨上去了。如果非要用reserve里面就应该去调用push_back:

push_back插入数据会依次把size涨上去。再看下一部分关于元素访问,[]这有强制检查越界,是断言;at是抛异常。front是访问第一个数据,back访问最后一个数据,日常用不到,因为[]都可以访问到。c++11还提供了data,和string中的c_str很像。再看下一部分,提供了尾插和尾删,都只有一个(string接口多是因为可能是字符的,可能是字符串的);这里没有提供头插和头删是因为要挪动数据,但可以用insert来头插和头删:

(1)可以在某个位置之前插入一个val,(2)在某位置之前插入n个val,(3)在某位置之前插入一段迭代区间。

erase有删除某个位置,删除一段区间。简单演示几个上述的介绍:

在上图的基础上想删除第三个位置的值可以:

现在想删除一个确定值,但是不知道在哪个位置,应该find一下,但发现没有find。如果自己写find就用迭代器,遍历找到了然后再删除就行了,但算法里也有函数模板,它的find写的是个函数模板

那为啥写算法里不写vector里,string又是自己写的?因为string的find很多样化,查字符不一定从0开始找,还可能找字符串,所以它单独实现,功能需求比较特殊。vector可以自己写但没有必要,因为它要和其它容器复用。(迭代器区间都是左闭右开,这找不到返回last,last是最后一个数据的下一个位置)所以这样使用:用pos接收返回值,找到了再删:

假设现在有多个3想删除怎么弄?

用下图的方法,不断的找,找到就删除,然后再从下一个位置找:

但这里运行的时候出错了,这里涉及迭代器失效的问题(后面细说),可认为v1.erase(pos)这里删除后这个位置的迭代器就失效了,所以再加就会出现坑。除非不让迭代器失效,现有阶段可每次从头开始找,但效率很低:

clear就是清掉数据但不释放空间,swap就是两个vector交换,assign就是把当前值清了以后重新赋值,下面简单演示一下:

再看看vector的扩容逻辑是怎么实现的:

基本上是1.5倍扩容。下面来看题目:

1.

136. 只出现一次的数字 - 力扣(LeetCode)136. 只出现一次的数字 - 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。 示例 1 :输入:nums = [2,2,1]输出:1示例 2 :输入:nums = [4,1,2,1,2]输出:4示例 3 :输入:nums = [1]输出:1 提示: * 1 <= nums.length <= 3 * 104 * -3 * 104 <= nums[i] <= 3 * 104 * 除了某个元素只出现一次以外,其余每个元素均出现两次。https://leetcode-cn.com/problems/single-number/submissions/这个就直接依次遍历用异或就可以

2.

118. 杨辉三角 - 力扣(LeetCode)118. 杨辉三角 - 给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。在「杨辉三角」中,每个数是它左上方和右上方的数的和。[https://pic.leetcode-cn.com/1626927345-DZmfxB-PascalTriangleAnimated2.gif] 示例 1:输入: numRows = 5输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]示例 2:输入: numRows = 1输出: [[1]] 提示: * 1 <= numRows <= 30https://leetcode-cn.com/problems/pascals-triangle/description/杨辉三角其实是一个二维数组,形象一点是一个三角形。杨辉三角的每一行数据个数是依次递增的,每一行第一个和最后一个是1,其余位置的值是由上一行一样的位置和前一个相加所得到的。先来说说C语言,这道题用c语言做非常折磨:

返回类型是二级指针,因为杨辉三角要求给你一个n行来生成n行的杨辉三角,它的本质是一个二维数组。二维数组只知道有numRows行,所以不能搞静态的要搞成动态的,那如何生成动态的二维数组呢?

先开一个一维的指针数组(首元素是int返回int*,首元素是int*返回int**,这也是为什么返回二级指针)然年第一行开一个,第二行开两个…分别让int*指向普通数组。还有后面两个参数问题,力扣为了保持统一,返回数组就得返回数组的行数,因为形参不影响实参,所以是int* returnSize。因为这的二维数组不是规则的,除了告诉行数还要告诉每一行的数据个数,每一行的数据个数存在一个数组里,数组开多大应该根据题目变化,所以还要开个一维数组存每一行数据个数:

这是要把数组返回出去但返回值已经被占了,所以再做输出型参数,malloc一个数组要拿指针,把指针地址传过来(malloc一个数组要被外面拿到,解引用拿值外面就改变了),可以见得C语言很麻烦。有人还会疑问说这个题多少行不是知道吗,每一行多少个不是也知道,为啥还要这两个参数?因为我们只是拿题来说,但换一个返回二维数组的题就不知道了,所以二维数组多少行通过returnSize拿,二维数组每一行有多少个通过returnColumnSizes拿。C++中用vector的vector来解决,来看看本质:

vector这有个a,size,capacity。vector的vector的a实例化是vector<int>*,数组中的每个数据都是vector<int>,vector<int>里面又是a,size,capacity。可认为这是一个对象数组,里面a实例化出int*,这样就可以很方便完成二维数组了。现在来实现:定义个vv,先用resize开numRows个空间,然后循环对每一行继续开,每次resize(i + 1),resize默认会给0,每一行第一个和最后一个给1。(vv[i][0]是先走vv.operator[](i),然后返回第i个位置的引用,然后再返回值返回的对象.operator[](0),然后返回值是int)。接下来从头开始走,处理值为0的,如果是0取上一行同一个位置和前一个位置的和。

    通过用vector感觉很爽,再来看一些vector更爽的地方。也发现vector使用学习成本比起学string大大降低了,因为它们设计有很多相似性,所以能用string也就能用vector。再来看看vector里面放string,它的本质和二维数组很像,只是平时可能不喜欢拿这里面的每个字符,所以不需要像二维那样走。vector<string>是一个对象数组:

它的a是string*,指向数组,数组中每个元素是string对象,一个个string又指向一个个字符串。下面结合题目来体会一下:

17. 电话号码的字母组合 - 力扣(LeetCode)17. 电话号码的字母组合 - 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。[https://pic.leetcode.cn/1752723054-mfIHZs-image.png] 示例 1:输入:digits = "23"输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]示例 2:输入:digits = ""输出:[]示例 3:输入:digits = "2"输出:["a","b","c"] 提示: * 0 <= digits.length <= 4 * digits[i] 是范围 ['2', '9'] 的一个数字。https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/submissions/这道题本质是一个全排列算法,比如输入的字符数组是"258",那会生成哪些相关的字母组合呢?

2对应abc,5对应jkl,8对应tuv。然后排列组合,a和j组,aj和t组,组合出ajt,那和vector<string>有什么关系呢?因为最后组合出这些字符串,这些字符串要存起来,就存到vector<string>里面。这里一会玩的是一个递归,从a递归下来是j,a和j加上变aj,然年aj再递归到t和t加上,再往下走没东西了就出来了第一个字符串,然后存vector里:

走完了回到t这一层,然后aj和u进行组合形成aju,然后走完再回到这一层和v组合形成ajv。和tuv这一层的都组合完了后再回到上一层,a和j已经组合完了,下一步a和k组合,然后再走一圈形成akt,aku,akv。完成后退回去再和l组合,然后al又组合成alt,alu,alv。这样走完后以a开头的就全出来了,退回第一层后再走b的所有组合,b走完回去后再走c的,全走完结束,这就是全排列组合。理解完后现在看题,首先给的digits是个数字字符串,给了数字后要找到数字串对应的字母串,可以弄个映射关系,定义个string数组,用缺省值初始化:

这要用递归,每一层就是一个递归,意味着走法是多层递归,数字串有多长递归就有多少层,因为一个数字对应完是一层,然后再和下一层组合。写个组合的子函数,参数怎么传呢?套个digits加下标[level],level代表它在第几层;还需要string combineStr表示组合出的字符串;走到头要存到vector里,所以还需要一个vector<string>& v:

为啥写子函数?因为给的letterCombinations中的参数不符合我们的需求,然后第一个传0,字符串最开始是空的,再传个vector,最后返回v:

(string不加引用是期望下一层加的东西不能影响上一层)子函数中,先确定是第几层,int num = digits[level] - '0';然后取映射的字符串,string str = strA[num]

然后依次取str里的字符,用循环,然后每次取到的和下一层组合,也就是上一层组合好的和这一层取到的组合传给下一层:

当level和digits.size()相等时就出结果了,结果放到vector里,然后返回;最后如果开始digits是空串时,就直接返回v:

通过递归展开图再来感受一下:

一开始level是0,取到num是2,然后取到的str是"abc",排列组合是任选一个,这是挨着全部走所以写个循环,i开始为0,combineStr是空string,组合将空+a传下去,递归调用……(+不影响上一层)。

2.vector深度剖析及模拟实现

    下面来看看模拟实现,实现前先简单看看源码(看stl30.tar.gz版本,平时包头文件相当于去库目录下找所包的文件,vector里有很多头文件,真正的核心在stl_vector.h中)

开始看源码的时候尽量别一行行挨着看,陷入细节当中。先看大框架,优先去看成员变量:

发现它的成员变量和我们一开始想的不太一样,也不知道这些成员变量代表的是什么意思,但可以明确是个迭代器,那下面再看看迭代器:

说明它的迭代器和string的玩法是一样的,用的原生指针,那为啥可以用原生指针呢?因为物理空间是连续的数组,解引用是当前位置,++可以到下一个位置(假设不知道这个类是什么样子的情况下,那就还要看一些核心的成员函数)。我们以前预想的是T*a, size_t size,  size_t capacity这样,现在不一样,所以下来还要探索一下start,finish,end_of_storage是干嘛的,此时应该去看构造函数(知道如何初始化)和插入接口函数:

从第一个构造看到相当于把三个指针初始化为空。

通过上图再结合名字,可以大致猜一下start指向第一个数据,end_of_storage指向空间结束,finish指向数据结束的位置:

从push_back中看到当finish!=end_of_storage的时候,把x放finish中,再++finish。如果满了去调insert_aux,扩容插入:

看到里面又判断了一下,结合来看之前的猜测正确。下面再来看一个图:

这个图基本表示了vector的架子,start指向空间的开始,finish指向数据结束的位置,finish - start是size,end_of_storage - start是capacity。我们实现定义结构时不用空间配置器,申请空间用new delete系列,结构上可以用_a _size _capacity,这里模拟实现时就和库保持一致:

再来写个构造函数:

想让数据跑起来再写个push_back,vector是模板,插入数据时参数给T,记得加引用,因为不知道是什么类型变量,若是自定义类型可提高效率。如果finish=endofstorage,说明满了要扩容,reserve到newcapacity,newcapacity是看原有的capacity是否为0,若为0就给4,不为0就2倍扩容。所以还要写个capacity函数,endofstorage-start就是capacity:

同理size也能写出来:

开好后直接在finish的位置赋值,然后再++finish:

还差个reserve,给个空间n,如果n大于capacity,开n个空间给tmp。如果原空间不为空,把数据拷贝过去,再释放旧空间,再指向新空间,此时finish=start+size(),endofstorage等于start+n:

再补两个迭代器,begin返回start的位置,end返回finish的位置:

现在一个基础的vector就好了,下面来测试一下:

发现崩了,来调试找找问题:

看到finish这里出了问题,锁定问题:

tmp好了后空间给start,start指向空间,size是finish-start弄的,此时finish是空,空减start是-start,负的加正的变为了0,引发了空指针解引用,但本质原因还是start变了,而capacity和size的计算又依赖start。所以第一种改进是这样:

此时没有改变start。但万一有人看这个顺序不顺眼改了顺序,所以还有一种方式,保存一下:

下面写一下析构,如果start不是空就delete了,然后都置为空:

下面实现[],断言一下,返回pos位置的值:

下面修改值测一下:

比如我们写了个print这样的函数,传了个vector过来,里面用范围for发现编不过:

因为权限放大了,这只能调用const版本的迭代器,我们还没写,所以补充一下:

下面补充一下insert,vector的insert不用下标,用的迭代器。实现在迭代器前插入一个val,首先断言一下(用新的结构写会让挪动数据变简单,之前玩下标的方法pos在0位置时存在无符号问题,新结构也不存在这样的问题,因为pos不可能为0,有效空间地址不可能为0),空间不够扩容,定义end指向finish的前一个,当end>=pos时,把end的值挪到end+1的位置,然后在pos的位置放数据,最后再++finish:

这样push_back也可以完成复用,完成后运行试一下:

但是崩了,刚开始空的时候有问题:

为空时按照逻辑走下来应该不会进去挪动数据,但这里进去了。不复用再看这样的一个例子:

这里还是同样的出现了问题,因为这里引发了迭代器失效问题,这是一个偶发性的问题。因为扩容的时候把数据拷贝下来,释放旧的空间,此时pos不对了,成了野指针:

这是迭代器失效的问题,所以要更新pos。所以如果有扩容要算一下相对位置,len=pos-start,扩完后更新一下pos:

此时复用的问题也解决了。现在想到第三个位置去插入:

这里没问题,(前面是内部迭代器失效问题,外部失效如何解决?)那此时*pos+=10可以吗:

有时候可以,有时候不可以,这虽然没什么问题,因为迭代器失效是偶然性问题,并且里面改变不会影响外面,形参不影响实参,形参的pos改变不会影响外面实参的pos。有人说给insert的pos加上引用,这样有问题,因为begin是值返回,返回的是一个拷贝,临时变量具有常性。如果加上const,pos=start+len又有问题。所以insert以后,迭代器可能会失效,记住insert以后就不要用这个形参迭代器了,因为它可能失效了,继续用是个高危行为。库中的insert带了返回值:

返回新插入元素的位置,一般不建议用,真要用就用返回值:

    下面来看erase的实现,比如有一段空间要删一个值我们怎么删呢?先断言一下,然后正常挪数据就行(但也会遇到迭代器失效问题),删完后再减减finish:

下面测试一下:

这个场景下没有迭代器失效的问题,因为删的是v1.begin(),这是临时的,没人再访问删完的迭代器。那什么场景下迭代器会失效呢?

上图用的时候感觉没有失效(这和前面的insert一样不知道什么时候失效,所以insert那里不管别人扩不扩容都认为失效了),但有些场景就会失效,比如:

删了再访问还++不合适,所以认为erase后迭代器也是失效的,不要轻易访问它(若上图改为std::vector<int>v1,库里面对这样的问题会强制检查直接崩溃)(认为erase以后,迭代器失效了,不能访问,vs进行强制检查,访问会直接报错)。看看g++与vs的区别:

(删除不抹数据)删除其实某种意义上来说不一定是野指针,更多的场景是这个位置的意义变了,不是指向原来位置了。vs下是直接不让玩了,g++下太过分才报错,比如删除所有偶数的场景:

vs下肯定报错,g++下好像还是没有问题,这可以删除干净是个偶然,现在这样改:

此时2没有删完,继续尾插6,这里报错了:

分析一下上面两个图的情况,第一个图是122345,删完第一个2后it已经失效了,但没有检查又加加,导致错过了2的位置;第二个图是因为该删6的时候变为12356,删完6后减减finish,it加加,然后it不等于end()就一直往后走,大量的越界崩了。还有人说加else可以解决这样的问题:

这样g++下问题虽然解决了,但真正的问题在于平台的移植性。如果非要用,文档中是通过返回值解决的:

返回的是指向刚被删除元素的下一个位置,所以正确这样写:

总结一下:vector的insert以后不管扩不扩容店都认为失效,erase后也认为失效,因为它认为删除后这位置的值变了,再访问有各种风险;vector的erase和insert迭代器对象后,不能再访问这个迭代器,我们认为他失效,访问结果是未定义的。下面写尾删,可直接调erase来解决:

再来写resize,string的resize可能不太重要,但vector的resize很重要。因为string一般不需要说开一段空间再初始化为某个值,vector很需要,开一段数组,把数组初始化一下,这是很常见的需求。所以会有个奇怪的现象,resize的初始值给:

开空间的时候可以自己不手动给值,不给是默认值,那可以const T& val = 0吗?因为T一定是int,可能是自定义类型,所以这样给值:

这个写法第一次见,本质是T类型的匿名对象,T如果是string就调string的默认构造(若没有默认构造则调不了,所以一般写一个类尽量写一个默认构造,这样会减少一些坑)。但T是int这里可以跑吗?理论上不可以,内置类型没有构造函数,但有了模板后C++对内置类型进行了升级,内置类型也有构造函数。可以感受一下:

下面来继续实现,resize分为这样的情况:

第一种是n小于size,直接改finish;第二种是n大于原来的大小,不管够不够都reserve,因为它内部有判断,然后循环边填数据,边++finish:

下面简单测试一下:

    下面来走一下拷贝构造:

运行崩了,很明显是浅拷贝的问题:

v和v1指向同一块空间,析构两次出现了问题。下面来实现一下深拷贝,这是一个当前要构造出的对象,把指针先初始化为0,然后开一块空间,拷贝数据,最后更新finish和endofstorage:

这样写刚才的样例不会崩溃了。有时候也会这样写:

再看下面一个例子:

程序崩溃了,因为赋值这里也是浅拷贝,所以写个赋值,可以写传统的写法:开空间,拷贝数据,释放旧空间。这里用新方法,v1=v2,v2先传给v调拷贝构造,拷贝出来的空间是v1想要的,所以交换一下,换它们的start,finish,endofstorage,最后返回*this:

再看下一个问题:

当前这个代码没有问题(单参数支持隐式类型转化),再添加一下:

此时崩溃了,猜测和扩容有关系,其实本质是一个隐藏的深拷贝问题。来画一下对象模型:

若是内置类型扩容拷贝下来没有问题,对于深拷贝的自定义类型,扩容时new一块空间,里面是一个个对象,这些字符串数组在堆上,原来的指向这些空间,扩容时用memcpy拷贝,把string每个字节拷贝下来,新的也指向这些空间,reserve中delete时会先调string的析构,再释放整个空间,此时tmp里的对象的指针变成了野指针。相当于我自己没有浅拷贝,但我里面的对象浅拷贝了。所以vector是深拷贝,但是vector空间上存的对象是string数组,使用memcpy导致string对象的浅拷贝,如何解决?

这里本质是调的string的赋值,string的赋值会去开一样大的空间一样大的值:

此时delete释放也不影响(若这里是内置类型也可以一个个的赋值下来,是简单的值拷贝)。memcpy版本的拷贝构造也有这样的问题:

所以这样改进:

总结解决方案就是:T如果是string这样的深拷贝的类,调用的是string的赋值重载,实现的是string对象的深拷贝。vector的vector也涉及这些问题,再画图感受一下:

vector里面有start,finish,endofstorage这三个成员,数组中也有这三个成员,它们名字一样但类型不一样。此时对它进行一个拷贝,如果用memcpy也有类似的问题,改了后就没有这样的问题了。

    再看下一部分,vector的构造中有这样两个构造(配置器不考虑):

先写用n个val初始化,可直接复用resize,但记得要初始化,否则三个指针随机值进resize有坑:

C++11的打补丁的好处也就出来了,构造的地方初始化列表那可以不写:

再写用迭代器区间初始化,不能这样写:

这样只能用vector的迭代器初始化,所以一个类模板中还可以套模板:

迭代器是左闭右开的,此时传什么类型迭代器都可以,测试一下:

编译报错(非法的间接寻址指这不是一个指针/不是用于解引用的对象),因为n个val初始化调到了迭代的函数,int解引用报错。因为模板在编译器上有原则,有现呈的匹配现呈,没有匹配接近的。n个val调函数时,一个是size_t和int,一个实例化为int和int,所以走了最匹配的。如果没有迭代器的函数就调用n个val的函数,因为没得选。这样也可以: vector<int> v(10u,1),加u后第一个10会被识别为无符号的int;vector<string> v1(10, "111")也没问题,这里涉及有得选和没得选的问题。那像上述出问题的地方有选则的情况如何解决呢?看看库中:

库里专门提供了int的,所以这样解决:

3.完整代码

//vector.h

#pragma once

namespace yxx
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

		vector()
		/*	:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)*/
		{}

		vector(size_t n, const T& val = T())
		/*	:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)*/
		{
			resize(n, val);
		}

		vector(int n, const T& val = int())
		{
			resize(n, val);
		}

		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		vector(const vector<T>& v)
		/*	:_start(nullptr)
			,_finish(nullptr)
			,_endofstorage(nullptr)*/
		{
			_start = new T[v.capacity()];
			//memcpy(_start, v._start, sizeof(T) * v.size());
			for (size_t i = 0; i < v.size(); i++)
			{
				_start[i] = v._start[i];
			}
			_finish = _start + v.size();
			_endofstorage = _start + v.capacity();
		}

	/*	vector(const vector<T>& v)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			reserve(v.capacity());
			for (auto e : v)
			{
				push_back(e);
			}
		}*/


		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_endofstorage, v._endofstorage);
		}

		vector<T>& operator=(vector<T>& v)
		{
			swap(v);
			return *this;
		}

		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _endofstorage = nullptr;
			}
		}

		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				reserve(n);
				while (_finish != _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}

		}

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					//memcpy(tmp, _start, sizeof(T) * size());
					for (size_t i = 0; i < sz; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + sz;
				_endofstorage = _start + n;
			}
		}


		void push_back(const T& x)
		{
			if (_finish == _endofstorage)
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);
			}
			*_finish = x;
			++_finish;
			//insert(end(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

		size_t capacity()const
		{
			return _endofstorage - _start;
		}

		size_t size()const
		{
			return _finish - _start;
		}

		T& operator[](size_t pos)
		{
			assert( pos < size());
			return _start[pos];
		}

		const T& operator[](size_t pos)const
		{
			assert(pos < size());
			return _start[pos];
		}

		iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start && pos <= _finish);
			if (_finish == _endofstorage)
			{
				size_t len = pos - _start;
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			_finish++;
			return pos;
		}

		iterator erase(iterator pos)
		{
			assert(pos >= _start && pos < _finish);
			iterator it = pos + 1;
			while (it != _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;
			return pos;
		}


	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _endofstorage = nullptr;
	};

	void print(const vector<int>& v)
	{
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector1()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		for (size_t i = 0; i < v1.size(); i++)
		{
			v1[i]++;
		}
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
	}


	void test_vector2()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(5);
		v1.push_back(5);
		v1.push_back(5);
		v1.push_back(5);
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
		v1.insert(v1.begin(), 100);
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<int>::iterator pos = v1.begin() + 3;
		v1.insert(pos, 300);
		*pos += 10;
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector3()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(5);
		for (auto e : v1)
		{
			cout << e << ' ';
		}
		cout << endl;
		//v1.erase(v1.begin());
		auto it = v1.begin()+4;
		v1.erase(it);
		cout << *it << endl;
		++it;
		cout << *it << endl;
		for (auto e : v1)
		{
			cout << e << ' ';
		}
		cout << endl;
	}

	void test_vector4()
	{
		vector<int> v;
		v.resize(10, 0);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;

		//int i = 0;
		//int j = int();    //0
		//int k = int(1);   //1
	}

	void test_vector5()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		vector<int> v1(v);
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<int> v2;
		v2.resize(10, 1);
		v1 = v2;
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector6()
	{
		vector<string> v;
		v.push_back("11111");
		v.push_back("22222");
		v.push_back("33333");
		v.push_back("44444");
		v.push_back("55555");
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<string> v1(v);
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector7()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
		string s1;
		s1.push_back(2);
		s1.push_back(2);
		s1.push_back(2);
		s1.push_back(2);
		vector<int> v2(s1.begin(), s1.end());
		for (auto e : v2)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_vector8()
	{
		vector<int> v(10,1);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}
//Test.cpp


#define _CRT_SECURE_NO_WARNINGS 1

#include <assert.h>
#include <iostream>
#include<vector>
using namespace std;

#include "vector.h"

int main()
{
	yxx::test_vector8();    //不同测试用例
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值