vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。因此能高效的进行随机存取,时间复杂度为o(1);但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内存块的拷贝,时间复杂度为o(n)。另外,当数组中内存空间不够时,会重新申请一块内存空间并进行内存拷贝。

连续存储结构:vector是可以实现动态增长的对象数组,支持对数组高效率的访问和在数组尾端的删除和插入操作,在中间和头部删除和插入相对不易,需要挪动大量的数据。它与数组最大的区别就是vector不需程序员自己去考虑容量问题,库里面本身已经实现了容量的动态增长,而数组需要程序员手动写入扩容函数进形扩容。

size()函数返回的是已用空间大小,capacity()返回的是总空间大小,capacity()-size()则是剩余的可用空间大小。当size()和capacity()相等,说明vector目前的空间已被用完,如果再添加新元素,则会引起vector空间的动态增长。

由于动态增长会引起重新分配内存空间、拷贝原空间、释放原空间,这些过程会降低程序效率。因此,可以使用reserve(n)预先分配一块较大的指定大小的内存空间,这样当指定大小的内存空间未使用完时,是不会重新分配内存空间的,这样便提升了效率。只有当n>capacity()时,调用reserve(n)才会改变vector容量。

 resize()成员函数只改变元素的数目,不改变vector的容量。

1. 空的vector对象,size()和capacity()都为0

2. 当空间大小不足时,新分配的空间大小为原空间大小的2倍。

3. 使用reserve()预先分配一块内存后,在空间未满的情况下,不会引起重新分配,从而提升了效率。

4. 当reserve()分配的空间比原空间小时,是不会引起重新分配的。

5. resize()函数只改变容器的元素数目,未改变容器大小。

6. 用reserve(size_type)只是扩大capacity值,这些内存空间可能还是“野”的,如果此时使用“[ ]”来访问,则可能会越界。而resize(size_type new_size)会真正使容器具有new_size个对象。

注意:不同的编译器,vector有不同的扩容大小。在vs下是1.5倍,在GCC下是2倍。

下边使用gcc测试vec扩容的代码:

#include<iostream>
#include<vector>
using namespace std;
int main()
{

        vector<int> vec;
        cout<<"0个元素时容器的容量为"<<vec.capacity()<<endl;
        for(int i=1;i!=100;i++)
{
        vec.push_back(1);
        cout<<i<<"个元素时容器的容量为"<<vec.capacity()<<endl;
}
        return 0;
}
~  

测试结果

vector容器详解

可以看到容器的容量不足时,新分配的空间为原来的两倍(vs上是0 1 2 3 4 6 9 13. ....,按1.5倍重新分配的)。

下边从 sgi-stl vector源码证实一下vector内存的分配规则

void push_back(const _Tp& __x) {
    if (_M_finish != _M_end_of_storage) { // 有备用空间
      construct(_M_finish, __x);    // 全局函数,将 __x 设定到 _M_finish 指针所指的空间上
      ++_M_finish;         // 调整
    }
    else
      _M_insert_aux(end(), __x);  // 无备用空间,从新分配再插入
  }

可以看到如果尾后插入元素时还有备用空间  则直接插入,否则调用M_insert_aux()重新分配。可以看到内存的分配规则。

即如果老得内存空间长度为0,则新内存空间长度为1,否则新的空间长度是老的空间长度的2倍。

vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x)
{
  if (_M_finish != _M_end_of_storage) {
    construct(_M_finish, *(_M_finish - 1));
    ++_M_finish;
    _Tp __x_copy = __x;
    copy_backward(__position, _M_finish - 2, _M_finish - 1);
    *__position = __x_copy;
  }
  else {// 没有备用空间
    const size_type __old_size = size();
    const size_type __len = __old_size != 0 ? 2 * __old_size : 1; //如果老得内存空间长度为0,则新内存空间长度为1,否则新的空间长度是老的空间长度的2倍
    iterator __new_start = _M_allocate(__len);
    iterator __new_finish = __new_start;
    __STL_TRY {
      __new_finish = uninitialized_copy(_M_start, __position, __new_start);
      construct(__new_finish, __x);
      ++__new_finish;
      __new_finish = uninitialized_copy(__position, _M_finish, __new_finish);
    }
    __STL_UNWIND((destroy(__new_start,__new_finish),
                  _M_deallocate(__new_start,__len)));
    destroy(begin(), end());
    _M_deallocate(_M_start, _M_end_of_storage - _M_start);
    _M_start = __new_start;
    _M_finish = __new_finish;
    _M_end_of_storage = __new_start + __len;
  }
}

频繁的压入元素,vector容器也会频繁的重新分配内存空间,可以通过用reserver(n)事先申请足够的容量,但reserver(n)申请的内存必须必当前的内存容量大。

  // 预留存储空间,若 __n 大于当前的 capacity() ,则分配新存储,否则该方法不做任何事。
void reserve(size_type __n) {
    if (capacity() < __n) {
      const size_type __old_size = size();
      iterator __tmp = _M_allocate_and_copy(__n, _M_start, _M_finish);
      destroy(_M_start, _M_finish);
      _M_deallocate(_M_start, _M_end_of_storage - _M_start);
      _M_start = __tmp;
      _M_finish = __tmp + __old_size;
      _M_end_of_storage = _M_start + __n;
    }
  }

 

vector容器的空间释放

由于vector的内存占用空间只增不减,比如你首先分配了10,000个字节,然后erase掉后面9,999个,留下一个有效元素,但是内存占用仍为10,000个。所有内存空间是在vector析构时候才能被系统回收。empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),vector所占用的内存空间依然如故,无法保证内存的回收。

 

 

相关文章: