討論標準模板庫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:當前容量 < 傳入容量,會根據新的容量建立記憶體,並複製。
當前容量 > 傳入容量,不理踩
其它情況不操作。