C++9.4 vector容器的自增长(size、capacity、reserve)
2015-06-08 10:16
645 查看
简介
vector容器,顺序存储,需要进行内存的分配,内存容量自动在增长,插入删除麻烦================================================================================================================================================================
一、vector容器的自增长
在容器对象中insert或压入一个元素时,该对象的大小增加1 。如果resize容器以扩充其容量,则必须在容器中添加额外的元素。标准库处理存储这些新元素的内存分配问题(最后面的例子)
为了支持快速的随机访问,vector容器的元素以连续的方式存放—-每一个元素都紧挨着前一个元素存储。
已知元素是连续存储的,当我们在容器内添加一个元素时,如果容器中已经没有空间容纳新的元素,此时,由于元素必须连续存储以便索引访问,所以不能在内存中随便找个地方存储这个新元素。于是,vector必须重新分配存储空间,用来存放原来的元素以及新添加的元素:存放在旧存储空间中的元素被复制到新存储空间里, 接着插入新元素,最后撤销旧的存储空间。如果vector容器在每次添加新元素时,都要这么分配和撤销内存空间,其性能将会非常慢,简直无法接受。(vector容器,顺序存储,需要进行内存的分配,插入删除麻烦)
对于不连续存储元素的容器,不存在这样的内存分配问题。例如,在list容器中添加一个元素,标准库只需创建一个新元素,然后将该元素连接在已存在的链表中,不需要重新分配存储空间,也不必复制任何已经存在的元素。(链式存储,插入删除方便,不需要进行内存分配)
一般而言,使用list容器优于vector容器,但是,对于大部分应用,使用vector容器是最好的。原因在于,标准库的实现者使用这样的内存分配策略:以最小的代价连续存储元素。访问元素的便利弥补了其存储的代价。
为了使vector容器实现快速的内存分配,所谓内存分配就是在刚创建一个容器时,对其进行内存分配,其实际分配的容量要比当前所需的空间多一些。vector容器预留了这些额外的存储区,用于存放新添加的元素。于是,不必为每个新元素重新分配容器。所分配的额外内存容量的确切数目因库的实现不用而不同(有的是原容量加倍,有的是原容量的3/2)。比起每添加一个新元素就必须重新分配一次容器,这个分配策略效率高。 所以,比起list和deque容器,vector的增长效率更高
1.vector实现内存分配用到的——-capacity 和 reserve成员
vector容器处理内存分配细节是其实现的一部分,该实现部分是由vector的接口支持的。vector类提供了两个成员函数:capacity 和 reserve,使程序员可与vector容器内存分配的实现部分交互工作。
size 指容器当前拥有的元素个数,而capacity 则指容器在必须分配新存储空间之前可以存储的元素总数。
reserve函数的功能是设置预留多大的内存空间
vector<int> ivec; cout<<"ivec:size:"<<ivec.size() <<"capacity:"<<ivec.capacity()<<endl; for(vector<int>::size_type ix=0;ix!=24;ix++){ ivec.push_back(ix); } //size 是24,capacity大于24 cout<<"ivec:size:"<<ivec.size() <<"capacity:"<<ivec.capacity()<<endl; // 结果: // 0 0 // 24 32
空的vector容器的size是0,而标准库显然将其capacity也设置为0.
当程序员在vector中插入元素时,容器的size就是所添加元素的个数,而其capacity则必须至少等于size,但通常大于size。
在上述程序中,一次添加一个元素,共添加24个元素,结果其capacity为32 . ivec容器当前状态如下图所示:
//设置预留额外的存储空间 ivec.reserve(50);//设置预留空间至少50 cout<<"ivec:size:"<<ivec.size() <<"capacity:"<<ivec.capacity()<<endl; //结果: 24 50 //size大小不变
while(ivec.size()!=ivec.capacity()) ivec.push_back(0); cout<<"ivec:size:"<<ivec.size() <<"capacity:"<<ivec.capacity()<<endl; //结果: //50 50 //因为使用的是预留的容量,所以vector不必做任何的内存分配工作,事实上,只要还有剩余的容量,vector就不必为其元素重新分配存储空间
因为使用的是预留的容量,所以vector不必做任何的内存分配工作,事实上,只要还有剩余的容量,vector就不必为其元素重新分配存储空间
从上述程序可以看出,我们已经耗尽了预留的容量因为 size与capacity值相等。此时,如果要再添加新的元素,vector必须为自己重新分配存储空间。一种策略是重新分配的容量的增幅为原容量的1/2,即 等于3/2原容量。还有策略就是在原来容量基础上加倍
ivec.push_back(42);//之前分配的所有空间已经耗尽了,此时再添加一个新的元素。 cout<<"ivec:size:"<<ivec.size() <<"capacity:"<<ivec.capacity()<<endl; //结果 // 51 100 //添加新的元素,vector必须为自己重新分配存储空间,重新分配的容量大小是加倍当前容量
当预留空间全部使用完,如果我们还要往里面添加新的元素时,vector会重新分配存储空间,而且,以加倍当前容量的分配策略实现重新分配(另外的内存分配的策略)
vector的每种实现都可以自由地选择自己的内存分配策略,然而,他们都必须提供reserve和capacity函数,而且必须是到必要时才分配新的内存空间。分配多少内存取决于其实现方式,不同库采用不同的策略实现
此外,每种实现都要求遵循以下原则:确保push_back操作高效得在vector中添加元素。从技术上来说,在原来为空得vector容器上n次调用push_back函数,从而创建拥有n个元素的vector容器,其执行时间永远不能超过n的常数倍、
例子 (很重要) 习题9.32
//下面容量自动分配时,标准库采用采取增幅为当前容量的1/2的分配策略(3/2 *原容量)。 //resize函数的使用 vector<string> svec; cout<<"ivec:size:"<<svec.size()<<"capacity:"<<svec.capacity()<<endl; svec.reserve(1024);//分配1024个内存大小 string text_word; while(cin>>text_word) svec.push_back(text_word); cout<<"ivec:size:"<<svec.size()<<"capacity:"<<svec.capacity()<<endl; svec.resize(svec.size()+svec.size()/2); cout<<"ivec:size:"<<svec.size()<<"capacity:"<<svec.capacity()<<endl; //解析该例子 //创建空的vector对象后,该对象的容量为0,将其容量设置为1024个元素,然后从标准输入设备读入一系列单词,最后将该vector对象的大小调整为所读入单词个数的3/2、 //如果程序读入的单词个数为256或512,那么size的大小分别是256和512.而capacity的大小都是1024。因为他们都小于对象所分配的最大的容量空间。 //下面执行resize函数的操作,因为resize函数的操作是调整容器内元素的个数,跟容量没有直接的关系。读入256或512时,resize之后分别是:384和768都是小于容器的容量1024的,所以容量不会变。如果容器的大小没有超出已分配的内存容量,从而调整大小只改变容器中有效的元素的个数,不会改变容量。 //如果读入单词个数为1000,读单词时不会产生容器内存不够情况,在使用resize函数会发生,使用resize函数后,需要1500个存储空间,已经超过了已经分配的容量(1024),所以容器会进行内存的重新分配,重新分配的容量是(3/2*原来的容量)即1536(1024+512)。 //如果读入的单词个数是1048,则在读第1025个单词时,因为用完已经分配的容量(1024),该容器的容量可能会增长为1536(1024+512)。resize调整大小后,需要的大小是1572个元素的存储空间。超过已经分配的容量1536.会再次重新分配,容量再次增长1/2*1536=768 现在的容量是1536+768=2304
//习题9.37 涉及到string类型的一些操作(9.6节) //假设希望一次读取一个字符并写入string对象,而且已知需要读入至少100个字符,考虑如何提高程序性能? //因为string对象中字符是连续存储的。所以为了提高性能,应该事先将对象的容量指定为至少100个字符大小, //以避免多次进行内存的重新分配,可以使用reserve函数(9.4节) #include <iostream> #include<string> #include<cctype> #include<vector> using namespace std; int main() { string str; str.reserve(100);//使用reserve函数预留内存大小 char strr; while(cin>>strr) str.push_back(strr); cout<<str<<endl; return 0; }
相关文章推荐
- C++对象模型之详述C++对象的内存布局
- 《C/C++工程师综合练习卷》之小试牛刀
- 《C/C++工程师综合练习卷》之小试牛刀
- PAT Broken Keyboard (20)
- 设计模式C++实现——代理模式
- c++对象内存布局
- c++对象内存布局
- C++调用java记录
- 对string对象或者vector对象执行sizeof运算
- 不可或缺 Windows Native (16) - C++: 函数重载, 缺省参数, 内联函数, 函数模板
- 给字符数组赋值的方法
- C++9.3.8 赋值与swap
- C++之静态成员变量和静态成员函数
- C++ 堆 和 栈 浅析
- 如何用VS2012编写c语言?
- 智能指针的标准之争:Boost vs. Loki
- C++继承、虚继承、虚函数类的大小问题
- C/C++类型转换
- C/C++常见修饰符
- 排序算法汇总(C/C++实现)