1. 程式人生 > >討論標準模板庫std::vector的容量/大小及其記憶體增量

討論標準模板庫std::vector的容量/大小及其記憶體增量

首先解釋一下容量/大小的區別:
  std::vector::capacity() : 指容器的能容納多少個。
  std::vector::size() : 指容器當前已裝多少個。
明白上面的意思思路就清晰多了。

這幾天有同事用vector的時候遇到問題,說是當push_back以後,以前引用了vector的地址就不能訪問。
造成以前引用的地址不能使用的原因估計大家都知道。
主要原因是vector有一個記憶體增量機制。


內增量機制是:為了滿路容器的連續記憶體訪問,當vector發現自己記憶體不夠用,就需要重新申請記憶體,
然後複製舊的資料過去,刪除舊的資料,重新指向新的資料地址。
當這個複製過程一結束的時候,以前引用了下標為0的vector記憶體地址就會不再是該資料地址了,
已被複制走的原有資料已刪除成為野記憶體。
這時需要重去引用一次。

關於這個增量機制網上有多種說法,
  1說法:按原有尺寸的2倍增長,即:new_capacity = capacity_size * 2;
  2說法:按原有尺寸的50%增加,即:new_capacity = capacity_size / 2 + capacity_size;
  3說法:按資料尺寸倍數增長,即 sizeof(class) * 2;
  4說法:按位元組來,即: (1024)/sizeof(class);


就此問題,我專門去看了看vector的原始碼。得到的結果是:2說法正確。
這點可以從std::vector::_Insert_n(iterator, size_type, const _Ty&)的原始碼中可以看出:

// 如果容量 < 大小 + 新數量增量
else if (_Capacity < size() + _Count)
{
  // 容量 = 容量 + 容量 / 2;
  _Capacity = max_size() - _Capacity / 2 < _Capacity ? 0 : _Capacity + _Capacity / 2; 
  // 容量 < 大小 + 新增數量
  if (_Capacity < size() + _Count)
    // 容量 = 大小 + 新增數量
    _Capacity = size() + _Count;
  // 申請新記憶體
  pointer _Newvec = this->_Alval.allocate(_Capacity);
  // 開始引用地址
  pointer _Ptr = _Newvec;
  // 複製與刪除過程
  ......
}

那麼如何解決我即想引用vector的地址又不需要經常去重新整理該地址呢?
同時又解決掉vector的複製問題,也解決掉有些類的複製構造?
方法還是有的。
這裡就有前面提到的容量/大小的因素了。
容量必然大於大小,是肯定的,即:capacity >= size;

方法1:
  建立std::vector容器,並呼叫std::vector::reserve(int n);
  告訴vector的直接增加n個數據並分配記憶體,
  這樣的話push_back數量在n以內,引用的地址不會發生變化。vector記憶體也沒有改變
  但如果事先告訴n的程式,這種方法是最好的。
  例:
    std::vector< int > vA;
    vA.reserve(10);
    vA.push_back(7);
    vA.push_back(6);
    vA.push_back(2);
    vA.push_back(9);
    int* pAddress = &vA[0];


方法2:
  通過建構函式告訴該容量容量
  各種帶引數構造類似,這裡不多舉例
  例:
    std::vector< int > vA(10);
    int* pAddress = &vA[0];
    pAddress[0] = 7; // 等於 vA[0] = 7;
    pAddress[1] = 6; // 等於 vA[1] = 6;
    pAddress[2] = 2; // 等於 vA[2] = 2;
    pAddress[3] = 9; // 等於 vA[3] = 9;


方法3:
  與方法1方法一樣。只是呼叫函式是:std::vector::resize(int n);

再說說resize與reserve的區別
resize:新的大小 < 現有大小,會刪除多餘的部分
        新的大小 > 現有大小,會在現有尺部插入新的資料,這時就有可能造成容量變化
        其它情況不操作。
reserve:當前容量 < 傳入容量,會根據新的容量建立記憶體,並複製。
         當前容量 > 傳入容量,不理踩
         其它情況不操作。