【STL】vector

vector源码实现

G2.9版本的vector源码

vector底层实现单向可扩容数组,其示意图和代码如下:
在这里插入图片描述

  • 注意finish的指向,即指向第一个绿色区域,end_of_range同理,指向后面第一个没有保存元素的区域
  • 留意 cpp size_type size() const {return size_type(end()-begin());},虽然调用end()和begin()函数相比直接调用变量start和finish浪费一些时间,不过这个时间是微不足道的,这样调用的好处是,如果将来改变了变量finish和start,那么函数size()得到的结果依旧没有改变。举一个生活中简单的例子就是a+b这个计算方法不会改变,但是两个加数可能会改变,例如下次可能就变成了c+d或者别的了。
  • vector初始长度为12字节,因为vector中有3个iterator,分别为start,finish,end_of_range
    vector’s iterator同样很好理解,iterator等效于vector节点元素取地址,代码和示意图如下:
    在这里插入图片描述

G4.9版本的vector源码

在这里插入图片描述

  • vector初始长度为12字节,因为vector继承的_Vector_base中包含了_Vector_impl,而_Vector_impl中有3个iterator,分别为_M_start,_M_finish,_M_end_of_range
  • 其中_Vector_impl公有继承于std::allocator,这个操作不严谨,因为public继承代表is-a关系,实际上_Vector_impl应该私有继承于std::allocator,因为_Vector_impl只是采用了std::allocator的方法而已
    vector’s iterator 看起来就很讨厌了…绕了一大圈iterator等效于vector节点元素取地址(耐心点沿着线条走就能看出来了),代码和示意图如下所示:
    在这里插入图片描述
    在这里插入图片描述
    相较于G2.9版的vector,G4.9的逻辑更加复杂,但是实现上基本没有区别,目前能看到的区别是G4.9版的iterator在偏特化时实例化为vector::iterator,G2.9G4.9版的iterator在偏特化时实例化为const T

vector扩容原理

(有些代码看不懂的看注释即可,不用全懂代码,掌握原理即可)
在这里插入图片描述
在这里插入图片描述

  • size代表保存元素的多少,capacity代表预留空间的多少
  • 使用resize(),容器内的对象内存空间是真正存在的;
    使用reserve()仅仅只是修改了capacity的值,容器内的对象并没有真实的内存空间(空间是"野"的)。此时切忌使用[]操作符访问容器内的对象,很可能出现数组越界的问题。
  • 但capacity==size时,意味着预留空间的不足,而vector是单向数组,元素连续放置,所以vector需要重新申请一个比旧空间大一倍的新空间来存放元素,但要是用户申请的元素个数为0…vector则会默认提供1个元素的空间
  • 例vector中已保存了8个元素,此时insert(5,ele),vector调用malloc分配一个可以保存16个元素的空间,将原位置0-原位置4的元素存放到新空间位置0-位置4,在新空间位置5放入ele,再将原位置5-原位置7的元素存放到新空间位置6-位置8,最后再释放原空间,并调整start,finish,end_of_range,假设空间无法分配16个元素的空间,则抛出异常。

注意事项

vector的迭代器类型以及相关操作的时间复杂度
随机访问迭代器
vector是可反转容器
随机访问每个元素 常量时间
末尾插入删除元素 常量时间
中间或开头删插元素 线性时间

如下代码为什么会出现core dump错误呢?

vector<int> vec{ 1,6,5,4,3,3,3,3,2 };
auto it=vec.begin();
while(it != vec.end()){ 
	if(6 == *it){
		vec.insert(it+1,4);
	}
	it++;
}
}

错误原因:

vector的迭代器在内存重新分配时将失效(它所指向的元素在该操作的前后不再相同),即当把超过capacity()-size()个元素插入vector中时,内存会重新分配,所有的迭代器都将失效。
原题中insert插入元素导致内存重新分配,it指向的内存已经被释放,对释放的空间再进行操作将导致core dump错误

解决方法:

//法一:使it指向插入元素对应的迭代器
it = vec.insert(it+1,4); 

//法二:在程序开头先预留好空间,避免调用函数时产生内存分配
vec.reserve(100);

如下代码为什么会抛出异常呢?

vector<int> vec{ 1,6,5,4,3,3,3,3,2 };
auto it=vec.begin();
while(it != vec.end()){ 
	if(6 == *it){
		it = vec.erase(it);
		cout << "*it : " << *it << endl;
		cout << "*(it + 1) : " << *(it + 1) << endl;
	}
	it++;
}
}

错误原因:

当vector删除元素时,指向被删除元素以后的任何元素的迭代器都将失效。

解决方法:

//使it指向删除元素之后对应的迭代器
it = vec.erase(it);

vector 为什么没有pop_front()?
vector模拟的是栈的功能,栈不能从前面出来吗,栈都是先进后出,后入先出的!实现一个那个函数可以很简单,却打破了他的初始理念。

data() 使用时的注意事项:

  • 函数返回一个直接指向内存中存储vector元素位置的指针,因为vector里面的元素都是顺序连续存放的,该指针可以通过偏移量来访问数组内的所有元素,但是在进行指针偏移时,即便是超出了范围好像也不会报错!使用gdb调试才发现会直接访问相应的内存,但不会出现错误。
  • 如果vector是const的,那么返回的是一个const指针,否则,返回普通的指针。
  • 要注意的是不要对一个空的数组返回的地址进行偏移操作,因为这可能导致错误。使用gdb调试发现的情况似乎是,并没有给vector分配实际的内存空间,因为data返回的是0x0,而不是正常的内存空间,故无法访问!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值