c++ vector模拟实现细节
1.reserve
预留空间,只有在想要的空间大于现有空间时才会扩容。但还要注意——start不能为空。
这里不能使用memcpy,因为底层其实是浅拷贝。所以应该自己进行深拷贝。tmp是一个数组,里面可能存放的是自定义类型,自定义类型的话,在其类中会有重载的赋值函数,所以直接使用如上佛如循环的方式即可。
但是注意,operator[]也要写。
还有一个,T tmp=new这里,应该是T*,指针类型!!!!
仔细看这个函数,这里有错,更新finish是有错。因为size是用finish减去start,但是走到这一步的时候,start已经更新了,所以计算size就会出错。所以最稳妥的办法就是提前将size记录下来。如下:
2.push_back
3.构造函数
这里一定要提供无参的构造函数,因为不可能每次创建容器的时候都能及时提供内容。
也想上面写的,防止出错,将三个指针在声明时提供nullptr缺省值,如下:
第二个构造函数,直接复用pushback,但注意这个构造函数的第二个参数,它的缺省值是一个匿名对象,匿名对象具有常性,所以要用const接收
4.insert——迭代器失效问题
end是iterator,我们是将T*定义成了它,所以end存放的是元素的地址。所以给对应位置赋值就是解引用。
有了insert之后,pushback就可以直接复用他了,如下/;
但是当插入时却报错了
如上调试可以看到,本来尾插传入的是_finish的值,但是走到这里,pos没跟随着_finish变,还是原来的值(原来容器为空,所以finish当时是null)。
为什么呢?因为在reserve中进行了扩容,会把新的地址给到finish,但是pos没随着修改!!这里就是pos失效了,也就是迭代器失效了!!!那怎么办??使用引用传参给pos吗?好像也可以
但其实不行/!!因为可能会这样传参:insert(v.begin());这样的话v.begin()要返回一个迭代器对象,传值返回过程中产生的临时对象具有常性,不可以。那该怎么办?只能是在reserve之后手动修改pos的指向。如下:
5.erase——迭代器失效
当测试一段去掉偶数的代码时,出错了
为什么呢?调试一下,会发先,删除了一个元素后,就会有++it和--finish的操作。这里会有两种情况:假设,有两个连续的偶数,那么删除了第一个之后,it++正好就跳过了第二个整数;而假设有一个偶数在末尾,那么删除之后,就会有++it和--finish,操作后,正好finish在it前面,还是满足it不等于finish,然后又进行while判断对非法it解引用,程序就会崩溃。那么怎么解决呢?在苦衷是通过返回被删除元素的地址来解决,如下:
将erase函数加上返回值,并在erase后让it接收,如下:
上面的问题也是一个迭代器失效的问题
6.reserve深度解析
这里详细说一下为什么不能使用memcpy。
如果是如下写法:
向vector中放置string类型的数据,当涉及到扩容时,就可以观察出现象:
当只插入四个字符串时,正常。但当插入五个涉及到扩容时就出错了
这是为什么呢?因为,memcpy是浅拷贝,memcpy(tmp,_start,size)这样只是将*start的值都给到了*tmp,但是,*tmp是一个string对象,里面有个字符指针,这样的拷贝会让*tmp中的指针和start指向同一块空间,然后又调用了delete[],而delete不仅仅是释放start指向的空间,还会对空间里面的对象进行析构,也就是会调用string的析构函数,这样一析构就导致start中的字符指针指向的空间被脂肪,那么tmp再去访问就不是原来的内容了
所以不能使用memcpy,只能使用string自己的赋值重载,也就是:
7.拷贝构造
有两个点,第一个,for里面的内容可以改为尾插。第二个,v是常量,无法调用非const的成员函数,所以要写个const的capacity函数。
如下:
8.迭代器区间构造函数
但是可能会用其他类的迭代器对容器进行构造,比如用char类型的迭代器对int容器进行构造。所以这个构造函数的参数可以使用模板,如下:
有了这个构造函数后,还会出现新的问题,比如现在是想给容器中插入10个0,如下:
为什么会调用这个函数呢??
对于这两个构造函数,v(10,0)参数是两个整型,相对于上面的构造函数,更匹配与模板函数,所以调用了下面的函数。
那怎么解决??专门提供一个int的构造函数,如下: