vector容器的自增长
概述
vector对象为了支持快速随机的访问,将元素以连续的方式存放(与数组相同)。
当我们向vector中添加一个元素时,如果容器已经没有空间容纳新的元素,此时,由于元素必须连续存放以实现快速索引,所以不能在内存中随便找个地方来存储这个元素。
于是,vector必须重新分配存储空间,存放在旧存储空间的元素需要复制到新的存储空间中,接着插入新元素,最后撤销旧的存储空间。如果vector每次添加新元素时,都要经过这些步骤的话,则其性能会非常差。
而list基本上不会有这样的顾虑,因为其在内存中是分散存储的,也就是链表的形式。
为了解决以上问题,stl的设计者,使用了一种方法,可以大大增加效率。具体就是加入了一种预先分配多一些空间的机制。比如:ivec起初是个空容器,插入一个元素后,这时ivec被分配的内存空间足以容纳50个元素,而接下来插入的49个元素,都无需重新分配内存。直到插入第51个元素,再分配一次内存,这时候ivec被分配的内存足以容纳100个元素。如此反复。事实证明,此种机制带来了显著的效率提升。
capcity和reserve函数
这是vector提供的两个成员函数。这两个函数用来为程序员提供接口,以用来操作内存分配策略。
capacity:用来获取在容器需要分配更多的存储空间之前能够存储的元素总数。
reserve:用来设定vector容器应该预留多少个元素的存储空间。
注:容量capacity和size的概念完全不同,capacity是容器的容量,即在必须重新扩大内存空间之前所能容纳的元素总数。而size则是当前容器中元素的个数。如ivec起初是一个空容器,插入一个元素后,size就为1,而capacity就为49.
考虑以下的例子:
vector<int> ivec;
cout <<"ivec:size:" <<ivec.size() <<endl;
cout <<"ivec:capacity:" <<ivec.capacity() <<endl;
for(vector<int>::size_type ix=0;ix!=24;++ix)
ivec.push_back(ix);
cout <<"ivec:size:" <<ivec.size() <<endl;
cout <<"ivec:capacity:" <<ivec.capacity() <<endl;
以上代码会输出:
ivec:size:0
ivec:capacity:0
ivec:size:24
ivec:capacity:32
可见,在ivec为空容器的时候,size为0,编译器将capacity也置为0;当在容器中插入1个元素后,重分配了空间,置capacity为50;24个元素插入完毕后,capacity为32,size为24。
容器的选用
一般来说,选用容器需考虑以下两个因素:
1、在容器的中添加或删除元素的代价。
2、在容器中随机访问。
程序使用这些操作的多寡,将决定选择哪种容器。
vector和deque提供了对元素的快速随机访问,但付出的代价是,在容器的任意位置插入或删除元素,会比在首部或尾部操作的开销更大。一切缘由于元素的连续存放(数组形式)。
list在任何位置都能快速的插入和删除元素,但付出的代价是,元素的随机访问开销较大。一切缘由于元素的非连续存放(链表形式)。
一些选择容器的法则:
1、如果程序要求随机访问元素,则应使用vector或deque容器。
2、如果程序必须在容器的中间位置插入或删除元素,则应采用list容器。
3、如果程序要求在容器首部或尾部插入元素,则应采用deque容器。
4、如果只需在读取数据时在容器中插入元素,插入完毕后需要随机访问,则可考虑在读取时将元素读入到list中,接着对list进行排序,使其适合顺序访问,最后将排序后的list复制到一个vector中。
5、如果程序既要随机访问又必须在容器中插入删除元素,则应该取决于这两种操作的多寡频率。如随机访问频率高,则选择vector或deque,反之选择list。
容器适配器
stl提供了三种容器适配器,所谓容器适配器,就是按一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。如stack适配器,可以让任何一种顺序容器以栈的方式工作(先进后出)。
1、stack栈适配器:严格的先进后出(压栈出栈)
2、queue队列适配器:严格的先进先出(队尾进,队头出)
3、priority_queue优先级队列适配器:可以设置元素优先级,将优先级高的元素放入优先级低的元素前。(按优先级进,队头出)