您的位置:首页 > 编程语言 > C语言/C++

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: