STL中vector的記憶體分配機制
關於vector,簡單地講就是一個動態陣列,裡面有一個指標指向一片連續的記憶體空間,當空間不夠裝下資料時會自動申請另一片更大的空間,然後把原有資料拷貝過去,接著釋放原來的那片空間(所以之前引用的vector的地址就失效了,不能訪問了);當釋放或者說是刪除裡面的資料時,其儲存空間並不會釋放,僅僅只是清空了裡面的資料。接下來,我會詳細地說說這些。
備註:本文的相關程式都是在windows 7+VS2008環境下測試。
- vector<int> arr;
- ofstream wf("1.txt");
- for(int i=0;i<100;++i)
- {
- arr.push_back(i);
- wf<<"capacity="<<arr.capacity()<<",size="<<arr.size()<<end;
- }
- wf.close();
再來看看1.txt中的資料:
capacity=1,size=1
capacity=2,size=2
capacity=3,size=3
capacity=4,size=4
capacity=6,size=5
capacity=6,size=6
capacity=9,size=7
capacity=9,size=8
capacity=9,size=9
capacity=13,size=10
capacity=13,size=11
capacity=13,size=12
capacity=13,size=13
capacity=19,size=14
capacity=19,size=15
capacity=19,size=16
capacity=19,size=17
capacity=19,size=18
capacity=19,size=19
capacity=28,size=20
capacity=28,size=21
capacity=28,size=22
capacity=28,size=23
capacity=28,size=24
capacity=28,size=25
capacity=28,size=26
capacity=28,size=27
capacity=28,size=28
capacity=42,size=29
capacity=42,size=30
capacity=42,size=31
capacity=42,size=32
capacity=42,size=33
capacity=42,size=34
capacity=42,size=35
capacity=42,size=36
capacity=42,size=37
capacity=42,size=38
capacity=42,size=39
capacity=42,size=40
capacity=42,size=41
capacity=42,size=42
capacity=63,size=43
capacity=63,size=44
capacity=63,size=45
capacity=63,size=46
capacity=63,size=47
capacity=63,size=48
capacity=63,size=49
capacity=63,size=50
capacity=63,size=51
capacity=63,size=52
capacity=63,size=53
capacity=63,size=54
capacity=63,size=55
capacity=63,size=56
capacity=63,size=57
capacity=63,size=58
capacity=63,size=59
capacity=63,size=60
capacity=63,size=61
capacity=63,size=62
capacity=63,size=63
capacity=94,size=64
capacity=94,size=65
capacity=94,size=66
capacity=94,size=67
capacity=94,size=68
capacity=94,size=69
capacity=94,size=70
capacity=94,size=71
capacity=94,size=72
capacity=94,size=73
capacity=94,size=74
capacity=94,size=75
capacity=94,size=76
capacity=94,size=77
capacity=94,size=78
capacity=94,size=79
capacity=94,size=80
capacity=94,size=81
capacity=94,size=82
capacity=94,size=83
capacity=94,size=84
capacity=94,size=85
capacity=94,size=86
capacity=94,size=87
capacity=94,size=88
capacity=94,size=89
capacity=94,size=90
capacity=94,size=91
capacity=94,size=92
capacity=94,size=93
capacity=94,size=94
capacity=141,size=95
capacity=141,size=96
capacity=141,size=97
capacity=141,size=98
capacity=141,size=99
capacity=141,size=100
資料有點多,提煉下就是這樣的:
capacity=1
capacity=2
capacity=3
capacity=4
capacity=6
capacity=9
capacity=13
capacity=19
capacity=28
capacity=42
capacity=63
capacity=94
capacity=141
看出其中的規律沒?對,就是每次擴容都是增加當前空間的50%(第一次除外);(也有人說記憶體的增量取決於實現,在不同的編譯器中可能就是capacity*2)
9+9/2=13;13+13/2=19;19+19/2=28……
其實STL的原始碼我們都可以看到的,具體就在你說安裝的編譯器目錄下,例如,我的VS2008是在:安裝目錄\VC\include下面。你也可以在VS中直接選中#include <vector>右鍵開啟。當然了,windows上的STL原始碼都是P.J. Plauger寫的(PS:很牛B的博士,百度你就知道),大家都說可讀性極差,我也這麼認為,我們這些菜鳥還是看GCC中的STL原始碼吧。
\VC\include\vector中是這樣擴容的:
- <span style="white-space:pre"> </span>if (_Count == 0)//這裡進行了判斷,但是什麼都不做,不知道為什麼???????
- ;
- elseif (max_size() - size() < _Count)//編譯器可以申請的最大容量也裝不下,丟擲異常_THROW(length_error, "vector<T> too long");
- _Xlen(); // result too long
- elseif (_Capacity < size() + _Count)//當前空間不足,需要擴容
- { // not enough room, reallocate
- _Capacity = max_size() - _Capacity / 2 < _Capacity
- ? 0 : _Capacity + _Capacity / 2; // try to grow by 50%,擴容50%
- if (_Capacity < size() + _Count)//擴容50%後依然不夠容下,則使容量等於當前資料個數加上新增資料個數
- _Capacity = size() + _Count;
- pointer _Newvec = this->_Alval.allocate(_Capacity);//申請新的空間
- pointer _Ptr = _Newvec;
- _TRY_BEGIN
- _Ptr = _Umove(_Myfirst, _VEC_ITER_BASE(_Where),
- _Newvec); // copy prefix <span style="white-space:pre"> </span>//拷貝原有資料到新的記憶體中
- _Ptr = _Ucopy(_First, _Last, _Ptr); // add new stuff<span style="white-space:pre"> </span>//拷貝新增資料到新的記憶體的後面
- _Umove(_VEC_ITER_BASE(_Where), _Mylast, _Ptr); // copy suffix
- _CATCH_ALL
- _Destroy(_Newvec, _Ptr);
- this->_Alval.deallocate(_Newvec, _Capacity);//釋放原來申請的記憶體
- _RERAISE;
- _CATCH_END
看看它的析構程式碼:
- ~vector()
- { // destroy the object
- _Tidy();
- }
- <span style="white-space:pre"> </span>void _Tidy()
- <span style="white-space:pre"> </span>{<span style="white-space:pre"> </span>// free all storage
- <span style="white-space:pre"> </span>if (_Myfirst != 0)
- <span style="white-space:pre"> </span>{<span style="white-space:pre"> </span>// something to free, destroy and deallocate it
- #if _HAS_ITERATOR_DEBUGGING
- <span style="white-space:pre"> </span>this->_Orphan_all();
- #endif /* _HAS_ITERATOR_DEBUGGING */
- <span style="white-space:pre"> </span>_Destroy(_Myfirst, _Mylast);//應該是銷燬vector中的每一個元素吧
- <span style="white-space:pre"> </span>this->_Alval.deallocate(_Myfirst, _Myend - _Myfirst);//釋放緩衝區的空間
- <span style="white-space:pre"> </span>}
- <span style="white-space:pre"> </span>_Myfirst = 0, _Mylast = 0, _Myend = 0;//指標全部歸零
- <span style="white-space:pre"> </span>}
二、如何強制釋放vector的緩衝區:
答案是可以的,既然析構時會釋放空間,那麼我們就可以換個方式呼叫解構函式。
- // //方法一、
- vector<int>().swap(arr); //交換後
- //方法二、
- {
- vector<int> temp;//臨時物件未初始化,其緩衝區大小為0,沒有資料 本人註釋:vector<int>temp=arr; //這樣swap之後arr才能保留原來的元素,並且收縮到合適的大小
- arr.swap(temp);//與我們的物件交換資料,arr的緩衝區就沒了。
- }//臨時變數會被析構,temp呼叫vector解構函式釋放空間
swap交換技巧實現記憶體釋放思想:vector()使用vector的預設建構函式建立臨時vector物件,再在該臨時物件上呼叫swap成員,swap呼叫之後物件myvector佔用的空間就等於一個預設構造的物件的大小,臨時物件就具有原來物件v的大小,而該臨時物件隨即就會被析構,從而其佔用的空間也被釋放。程式碼如下(http://blog.csdn.net/sunmenggmail/article/details/8605538):
- vector< T >().swap(X)
- /*******************************************************************
- * Copyright (C) Jerry Jiang
- *
- * File Name : swap.cpp
- * Author : Jerry Jiang
- * Create Time : 2012-3-24 4:19:31
- * Mail : [email protected]
- * Blog : http://blog.csdn.net/jerryjbiao
- *
- * Description : 簡單的程式詮釋C++ STL算法系列之十五
- * 成員函式swap實現容器的記憶體釋放
- *
- ******************************************************************/
- #include <iostream>
- #include <algorithm>
- #include <vector>
- #include <iterator>
- usingnamespace std;
- int main ()
- {
- int x = 10;
- vector<int> myvector(10000, x);
- //這裡列印僅僅是元素的個數不是記憶體大小
- cout << "myvector size:"
- << myvector.size()
- << endl;
- //swap交換函式釋放記憶體:vector<T>().swap(X);
- //T:int ; myvertor代表X
- vector<int>().swap(myvector);
- //兩個輸出僅用來表示swap前後的變化
- cout << "after swap :"
- << myvector.size()
- << endl;
- return 0;
- }
本人註釋:我發現很多文章可能在轉載的過程中出錯了,因為這樣swap的話,myvector就被清空了,而不是把記憶體收縮到合適,原來的元素也沒保留,我通過除錯和檢視別的文章發現,應該對上述程式碼做如下修改:
#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
using namespace std;
int main()
{
int x = 10;
vector<int> myvector;
myvector.push_back(1);
myvector.push_back(1);
myvector.push_back(1);
myvector.push_back(1);
myvector.push_back(1);
//列印元素的個數和容量大小
cout << "myvector size:"
<< myvector.size()
<< endl;
cout << "myvector capacity:"
<< myvector.capacity()
<< endl;
vector<int>(myvector).swap(myvector);
//兩個輸出用來表示swap前後的變化
cout << "after swap :"
<< myvector.size() << " "
<< myvector.capa