1. 程式人生 > >STL淺析——序列式容器vector的構造和內存管理: constructor() 和 push_back()

STL淺析——序列式容器vector的構造和內存管理: constructor() 和 push_back()

技術 eal ace name des 分享 names private rst

  咱們先來做一個測試capacity是容器容量,size是大小:

#include <iostream>
#include <vector>
using namespace std;

int main(){
    vector<int> result;
    for (int i = 0; i < 17; i++)
    {
        result.push_back(i);
        printf("element count:  %d\t", result.size());
        printf("capacity:  %d\t
", result.capacity()); printf("first element‘s address: %x\n", result.begin()); } return 0; }

  運行結果:

  技術分享

  可以觀察到每次容器滿了需要擴容的時候,容量總是呈現兩倍增長,而且每次擴容,容器第一個元素所在地址都會發生改變,由此我們知道,容器的擴容時實際是另外尋找一片更大的空間,VS的如下:

  技術分享

  擴容的倍數不一樣VS為1.5倍擴容,最好的擴容倍數是黃金分割比,1.618倍,當然也不可能那麽精確。。。

  vector 缺省使用 alloc (實際上是 宏__STL_DEFAULT_ALLOCATOR(_Tp) 的typedef,但是該宏本質還是第二級空間配置器最終封裝而成的 alloc) 作為空間配置器,並據此定義了一個 _Base。

class vector : protected _Vector_base<_Tp, _Alloc> 
{
...
private:
      typedef _Vector_base<_Tp, _Alloc> _Base;
...
//以前是:
    typedef simple_alloc<value_type, Alloc> data_allocator;
//最新版本是進行了進一步封裝,分開了基本參數和泛型操作函數
}

  vector 提供了許多constructors,其中一個允許我們指定空間大小與初值:

  
//就是這個,允許我們指定空間大小和初值
vector(size_type __n, const
_Tp& __value, const allocator_type& __a = allocator_type()) : _Base(__n, __a) { _M_finish = uninitialized_fill_n(_M_start, __n, __value); } //上面 uninitialized_fill_n 這個函數,藏得很深在stl_uninitialized.h裏面,實際該函數還是對fill函數的一次封裝
//最終調用的是fill函數,
在 stl_alogbase.h 這個頭文件下面,如下,
//不過這個文件夾下面有很多fill函數,我想應該是uninitialized_fill_n的作用還包括型別判斷,根據型別不同調用不同的fill函數
void fill(_ForwardIter __first, _ForwardIter __last, const _Tp& __value) {
  __STL_REQUIRES(_ForwardIter, _Mutable_ForwardIterator);
  for ( ; __first != __last; ++__first)
    *__first = __value;
}

  我們以 push_back() 函數將新元素插入vector尾端時,該函數首先檢查是否還有備用空間,如果有則直接在備用空間上構造元素,並調整叠代器finish,使得vector變大。如果沒有備用空間就擴充:

  void push_back(const _Tp& __x) {
    if (_M_finish != _M_end_of_storage) {
      construct(_M_finish, __x);  //直接placement構造元素,移動_M_finish指針
      ++_M_finish;
    }
    else
      _M_insert_aux(end(), __x);  //重新構造整個vector並插入元素
  }
 //表示不懂為啥要重載一次push_back()。。。
void push_back() { if (_M_finish != _M_end_of_storage) { construct(_M_finish); ++_M_finish; } else _M_insert_aux(end()); }

  至於 _M_insert_aux 函數,有點復雜,但是也不難理解,如下:

vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x)
{
  if (_M_finish != _M_end_of_storage)  //有備用空間 
 {
  //在備用空間起始處構造一個元素,並以vector最後一個元素為其初始值 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,則配置一個元素大小
//如果原大小不為零,則配置原來大小的兩倍
   //前半段用來存放原始數據,後半段用來存放新數據

   iterator __new_start
= _M_allocate(__len); iterator __new_finish = __new_start; __STL_TRY
  {
    //原vector內容拷貝到新vector __new_finish
= uninitialized_copy(_M_start, __position, __new_start); construct(__new_finish, __x);  //為新元素設定初始x ++__new_finish;          //調整水位
    //安插點的原內容也一起拷貝 __new_finish
= uninitialized_copy(__position, _M_finish, __new_finish); } __STL_UNWIND((destroy(__new_start,__new_finish), _M_deallocate(__new_start,__len)));
  //銷毀,釋放原vector destroy(begin(), end()); _M_deallocate(_M_start, _M_end_of_storage
- _M_start);
  //調整叠代器,指向新的vector _M_start
= __new_start; _M_finish = __new_finish; _M_end_of_storage = __new_start + __len; } }

  因此可以明白,對 vector 的任何操作,一旦引起空間重新配置,則指向原 vector 的所有叠代器都會失效。

STL淺析——序列式容器vector的構造和內存管理: constructor() 和 push_back()