一、整体逻辑
1、构造、析构 push_back() reserve() size() capacity() []
2、使用3个指针的好处 insert erase(0位置时不会死循环,地址一定不为0) 迭代器表示范围
扩容时,指针位置会变,pos变为野指针(函数内外都失效,外部因为传值),导致迭代器实现
注意:inserat以后就不要使用这个pos迭代器了,但是可以通过接收返回值更新pos
3、resize n个值构造 迭代器区间构造 拷贝构造 赋值重载
二、构造/析构/尾插
1、简单的构造
直接将vector的3个指针成员置空即可。
可以使用C++11中的缺省值,在初始化列表时就会自动完成。
2、析构
delete[]对于_start指向的一段数据,分别调用析构函数完成清理,最后回收空间。
3、reserve/[]/size/capacity
1、_start非空时才挪动数据,这里挪动数据不能简单的使用memcpy拷贝内存。
如果是自定义类型,一个指针指向开辟的堆空间,memcpy会导致指针的浅拷贝,即指针指向同一块空间,进而导致多次析构报错。
应该采用循环赋值的方法,每次都调用vector中类型T的赋值运算符重载函数(对象数组中的每个对象分别调用),在重载函数中会完成深拷贝。
2、最好提前保存一下sz,即元素个数。因为扩容后_start和_finish _end_of_storage就没有关系了,若此时再调用size()得到个数是不准确的。
三、insert/erase
1、insert
利用迭代器pos指定要插入的位置,此时如果要扩容,就会导致迭代器失效(函数内部失效)。
即pos还在原来_start那一段空间的某个位置,扩容后,_start重新指向一块空间,再在pos位置插入就是错误的。
可以先用gap保存一下pos的相对位置,如果扩容,就更新一下pos
最后返回pos即更新后的位置
2、erase
返回pos,即删除那个元素的下一个位置的元素。
总结:对于vector,使用erase和insert迭代器对象后,迭代器就会失效,不能再访问,访问的结果是未定义的。但是可以在insert和erase后接收返回值来更新迭代器(外部)。
四、resize/区间/拷贝/=
1、resize
插入n个val,可以使用不用传参默认构造的T()匿名对象作为缺省值。
C++中,将内置类型升级,即int double等,也可以生成T()的匿名对象。
n<=size()时,直接调整_finish的位置即可,不用抹除数据。
n>size()时,先reserve(n),reserve内会判断n是否大于capacity(),如果需要,就进行扩容。
然后进行循环赋值插入,或复用尾插。
也可以完成n个val的初始化,复用resize即可。
2、迭代器区间构造
定义一个新的模板参数InputIterator,first和last表示要插入的迭代器区间,可以是别的类型的迭代器,因此重新定义为InputIterator。
3、拷贝构造
循环赋值或复用尾插
4、=重载
使用现代写法,先让调用拷贝构造得到形参v,然后交换。
目录