C++ STL vector 底层原理

vector是我们最经常用到的STL容器之一
其能够动态改变数组大小 比起C的数组方便了很多

三个指针
vector中有三个迭代器first,last,end, 分别指向了数组的开头, 数组的结尾+1以及数组的最大容量结尾
迭代器这里可以理解为是指针
当我们有了这三个指针之后, 一些vector的信息很快就能够得到

int first,last,end;		//假设存的是指针地址 运算只涉及到相对大小 所以直接用int距离
unsigned int size(){		//通过首尾 很容易得出size
	return last-first;
}
bool empty(){	//可以直接判断是否为空
	return last==first;
}
unsigned int capacity(){	//得到当前数组的容量
	return end-begin;
}
//...等等函数

其中上面这个capacity指的是当前vector的数组长度, 没想到吧, 其实vector底层还是用静态数组来实现的, 大概意思就像这样:

int* data=new int[capacity];

那么如何动态改变容量, 便是vector的核心

resize函数
当我们进行vector的push_back()操作的时候, 就有可能使得vector的静态数组超出其容量
在了解之前, 我以为vector的扩容是在后面申请一段新的空间, 然后通过链式合并到原来的
但是其实其原理非常简单, 在扩容的时候其实是新申请了一段更大的静态数组, 然后把原来的数组的东西copy过去, 最后删掉原来的数组, 大概是像这样的:

void resize(){
	//此时静态数组已经满了 即last==end
	int* new_data=new int[new_capacity];	//创建一个更大的内存的静态数组
	for(int i=0;i<last-begin;i++)	//把原来的元素复制过去
		new_data[i]=data[i];
	delete[] data;	//把原来的数组空间释放
	data=new_data;	//然后把新的空间拿来用
	end=new_capacity+begin;	//扩容后了
}

由此可以看出, vector的扩容每次都是O(n)的操作, 这样就会使得我们以为的O(1) push_back()不那么香了, 因此为了降低操作复杂度, 显然我们是不能频繁的进行resize的

resize的频率显然取决于new_capacity是多少

  • 假如该值总为capacity+1, 那么每次push_back()都要申请大一点的空间, 相当费力
  • 假如该值为一个非常非常大的数, 那么就不用resize, 但是会浪费大量的空间

可以看出上面两种都是不大行的

因此C++的扩容采取的是倍数扩容, 扩容倍数在[1.5,2] 之间都是合理的
这是为了降低每次扩容的次数, 但是又不会过度浪费空间

在VS中, 该倍数为1.5 而在GCC中, 该倍数为2
这里VS的1.5倍考虑了内存的回收
想想看, 如果当倍数为2的时候, 那么每次扩容是这样的:

1 2 4 8 16 32 64...

显然, 每次新的值需要的内存大小, 比前面所有释放了的空间总和都要大
比如现在扩容到8, 以前释放过的内存大小总和也才1+2+4=7

但是当倍数为1.5的时候

1 2 3 5 8 12 18

可以看出, 在8以后的每次申请内存过程中, 前面已经释放过的内存总能比它大, 也就是能够回收内存

push_back()大概

void push_back(int x){
	if(end==last)resize();	//容量不够了
	data[last++]=x;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值