要说清这个问题,先抛出另外一个问题:vector和普通的数组有什么区别?
你可能会说:大家都知道,vector是动态数组~~
没错! 但是怎么个动态法?怎么实现的?
好,先说一下普通数组,比如:int arr[10]或者int *arr = new int[10]; 其实是你到内存里占了10个int大小的地方(空间);
当然,你可以这样操作:int a = arr[100]; arr[100] = 10; 这样编译器并不会报错,最多会警告你----你有越界行为。
也就是说,你一开始说好了,你只占10个大小的地方,后来你出尔反尔,又去访问或者操作第100个地方(为了说明问题,事实上是第101个),那么内存的“管理员”他没有不让你操作的权利,只是给你出示了“黄牌警告”,告诉你:你去操作吧,出了问题概不负责!!!
这就是数组越界问题,越界会造成未知的问题(程序崩溃等等),即你写的程序不受你控制了,或者不是你想要的结果(比如:你越界访问的地方是别人的地方,如果别人也没做“防护”,那么你修改了那个地方的值,就会让别人的会出现未知错误)。为了避免这个问题,那就老老实实的在那个10个大小的地方活动。
但是,在实际应用中,程序员的确有这样的需求:我开始是分配了10个大小的地方,但是我想给它后面添加一个或者给中间某个位置插入一个,变成连续11个数值。
我再描述一下这个需求:在一个连续的数组添加元素,还能保证新的数组仍然在内存连续,也就是说用下标可以访问(arr[10]访问第11个元素)。
vector就实现了这样的功能,当然vector功能不止这一个,还有很多操作(push_back,insert等)。
那么,vector是怎么实现这样的功能的?可以参考《STL源码剖析》
我们以push_back()为例,
其中size就是实际存入数据空间的大小,capacity是容量,即能容纳的数据个数。下面看一下push_back()的过程:
在insert_aux里:
这个过程就是容量扩充(备用空间不够了,就重新开辟更大的空间,把旧的拷贝进去,再把旧的释放了,有了更大的新空间,就可以连续的插入和追加元素了)
所以这个过程:重新配置->拷贝数据->释放旧的空间
如果每添加一个元素都这样操作,那效率太低了,索性一次多分分配些空间。
但是注意:
1.vector可以用下标访问元素,但是,只能访问size以内的(比如,size=10,capacity=15,就不能arr[12]这样会报错)
2.一旦扩容,原来空间就已经不存在了,数组首地址也不存在了,因为旧的被复制到新的空间,旧的就被释放了。
下面回归正题:size和capacity有什么区别?
size是真实元素的所占的空间打开,capacity是整个可容纳的空间大小;
如第一个图:
size = finish - start
capacity = end_of_storage - start
下面看一下不同编译器capacit大小分配情况:
这是vs2017的运行结果:
vector<int> arr(10,1);//10个空间,初值都是1
cout << "size=" << arr.size() << endl;
cout << "capacity=" << arr.capacity() << endl;
arr.push_back(2);//向后面添加一个元素2
cout << "size=" << arr.size() << endl;
cout << "capacity=" << arr.capacity() << endl;
这是ubantu16,g++ 5.4的运行结果:
为什么一样呢?
这个编译器有关系,在《STL源码剖析》上可以找到答案:
但是再vs2017上,capacity在满的时候,扩充的大小为原来的一半,而不是2倍。