一、基本概念
reverse()和resize()是用来给容器vector、list和string预留空间或调整他们的大小:reserve()用来保留(扩充)容量,他并不改变容器的有限元素个数;resize()则调整容器大小(size,有效元素的个数),而且有时候会增大容器的容量。
首先我们得搞清楚“容量”和“容器”以及“有效元素”的概念。
容量是为了减少那些使用连续空间(线性空间)存储元素的容器在增加元素时重新分配内存空间的次数的一种机制,即当增加元素且剩余空间内存不足时,按照一定比例(通常是原来容量的1.5或2倍)多分配一些空闲空间已备将来再增加元素时使用,以提高插入操作的性能。一个具有多余容量的std::vector<T>得典型内存映像如图一所示。
图中迭代器start和finish之间的元素就是容器的有效元素(实际上是有效元素对象本身的内存单元),而strart和end_of_storage之间的空间就是该容器的总容量。finish和end_of_storage之间的空闲就是冗余容量,冗余容量不属于容器。
多余出来的容量(空闲存储空间)是未经初始化的(并不是调用元素类型的默认构造函数来初始化)。我们可以从std::vector<T>的size()和capacity()这两个成员函数的实现上看出容器所管辖元素空间和容量的区别:
size_type capacity() const {return (start==0?0:end_of_storage);}
size_type size() const {return (start==0?0:finish);}
一个容器可以没有任何有效元素,但是却有许多冗余的容量;或者一个容器可以没有任何冗余的容量,全是有效元素;或者既有一些有效元素又有一些冗余容量。
二、用途
下面我们来看看reserve()和resize()到底做了什么。
reserve()原型如下:
void reserve(size_type n);
其中n就是用户请求保留的总容量的大小(在不重新分配内存的情况下可容纳元素的个数)。reserve()可按如下情况实现:
(1)如果n大于容器现有的容量(即capacity()),则需要在自由内存区为整个容器重现分配一块新的更大的连续空间,其大小为n*sizeof(T),然后将容器内所有的有效元素从旧位置全部复制到新位置(调用拷贝构造函数),最后释放旧位置的所有存储空间并调整容器对象的元素位置指示器(即让那3个指针指向新内存区的相应位置)。也就是说,如果请求容量比原有容量大的话,结果是容器的冗余容量加大(即end_of_starage指针所指的相对位置发生了变化),而容器本身的有限元素不会发生任何变化。
(2)否则什么都不做。如图二所示。
resize()顾名思义,他调整容器的大小(size),有时也会扩大容器的容量,话句话说,不管容器当前包含多少个有效元素,也不管容器的冗余容量是多少,他都将容器的有效元素调整为用户指定的个数。resize()原型如下:
void resize(size_type n,const T& c=T());
其中n就是最后要保持的元素个数,如果需要新增元素的话,c则是新增元素的默认初始值。下面是resize()的实现策略:
(1)如果n大于容器当前的大小(即size()),则在容器的末尾插入(追加)n-size()个初始值为c的元素,如果不指定初始值,则用元素类型的默认构造函数来初始化每一个新元素(可能会引起内存重分配以及容器容量的扩张)。
(2)若果n小于容器当前的大小,则从容器末尾删除size()-n个元素,但不释放元素本身的空间,因此容量不变。
(3)否则什么也不做。
3、注意事项
我们可以将容器看做一个区间,我们知道,出了调用容器的某些方法可以改变容器的大小,在容器外部没有任何方法可以做到这一点。因此如果想使用迭代器在冗余量的空间上通过赋值来给容量增加元素的话,结果会大失所望。我们来举个栗子:
std::list<int> i1;
std::vector<int> v1;
for(int c=0;c<10;c++)
i1.push_back(c);
v1.reserver(i1.size());//预留空间,但是并没有改变容器的大小,预留空间未初始化
std::copy(i1.begin,i1.end,v1.begin);//拷贝赋值
std::copy(v1.begin,v1.end,ostream_iterator<int>(std::cerr,"\t"));
这段程序显然是错误的,虽然v1.reserve()为vector预留了空间,但是改变的只是容器的容量。同时在copy()算法中对容器元素复制也不会改变容器的大小(毕竟算法不直到容器的存在,他只知道区间),因此复制后容器的size()仍然为0,虽然list的元素已经被复制到了为vector预留的空间上,结果可想而知;没有输出任何东西,!vector在复制前后的状态变化可以用图三来说明。
因此reserve()不能与赋值运算符配合,比如不应该与简单的copy()算法合作,而应该与push_back()、insert()等操作合作,或者与使用insert(迭代器的copy()算法合作。例如:
v1.reserve(i1.size());//预留空间,但是并没有改变容器的大小,预留空间并不初始化
std::copy(i1.begin(),i1.end,std::back_inserter(v1));
这样的话,copy()算法通过insert迭代器不仅给容器增加了元素,而且也间接的改变了容器的大小。
然而,resize()与赋值运算符及insert()、push_back()等都可以合作,例如上面的例子可以按如下编写:
std::list<int> i1;
std::vector<int> v1;
for(int c=0;c<10;c++)
i1.push_back(c);
v1.resize(i1.size());//调整容器大小
std::copy(i1.begin,i1.end,v1.begin);//拷贝赋值
std::copy(v1.begin,v1.end,ostream_iterator<int>(std::cerr,"\t"));
这次,就可以输出正确的结果了,如图四所示。
相反的,使用resize()缩减容器大小也是类似的。