1. 程式人生 > >STL的增長機制、以及記憶體釋放機制

STL的增長機制、以及記憶體釋放機制

Vector是STL中最常見的容器,它佔用的是一塊連續分配的記憶體,從資料儲存角度來講,和陣列極其相似,不同的是:陣列是靜態分配空間,一旦分配了空間的大小就不可以再改變了,而VECTOR是動態分配空間,隨著元素的不斷增加,它會按照自身的一套增長機制不斷擴充自身的容量。

擴容機制:

      按照容器現有容量的一倍進行增長。由於vector容器分配的是一塊連續的記憶體空間,每次容器的增長並不是在原有連續的內容空間後進行簡單的疊加,而是重新申請一塊更大的新記憶體,並把現有容器中的元素逐個複製過去,然後銷燬原有記憶體。

Eg:vector初始化時申請的空間大小為6,存入了6個元素,當向vector中插入第7個元素“6”時, vector會利用自己的擴容機制重新申請空間,資料存放結構如圖1.

優點:動態增長,可不斷增長,大小不受限制;

缺點:重新分配記憶體的代價有多高昂?它涉及如下四個步驟: 

·          為需要的新容量分配足夠的記憶體;

·          將元素從原來的記憶體拷貝到新記憶體中;

·          銷燬原來的記憶體中的元素;

·          歸還原來的記憶體。 

        如果元素的數目為n,那麼我們知道步驟(2)和(3)都要佔用O(n)的時間,除非分配或歸還記憶體的代價的增長超過O(n),否則這兩步將在全部執行時間中佔居支配地位。因此我們可以得出結論:無論用於重新分配的容量(capacity)是多少,重新分配一個尺寸(size)為n的vector需要佔用O(n)的時間。 

這個結論暗示了一種折衷權衡。假如在重新分配時請求大量的額外記憶體,那麼在相當長的時間內將無需再次進行重新分配,因此總體重新分配操作消耗的時間相對較少,這種策略的代價在於將會浪費大量的空間,省時間費空間;另一方面,我們可以只請求一點點額外的記憶體,這麼做將會節約空間,但後繼的重新分配操作將會耗費時間,省空間費時間。換句話說,我們面臨一個經典的抉擇:拿時間換空間,或者相反。

Pushback實現:

        vector是用陣列實現的,每次執行push_back操作,相當於底層的陣列實現要重新分配大小(即先free掉原儲存,後重新malloc);這種實現體現到vector實現就是每當push_back一個元素,都要重新分配一個大一個元素的儲存,然後將原來的元素拷貝到新的儲存,之後在拷貝push_back的元素,最後要析構原有的vector並釋放原有的記憶體。

元素是順序儲存的!

vector記憶體釋放機制:

vector中的內建有記憶體管理,當vector離開它的生存期的時候,它的解構函式會把vector中的元素銷燬,並釋放它們所佔用的空間,所以用 vector一般不用顯式釋放——不過,如果你vector中存放的是指標,那麼當vector銷燬時,那些指標指向的物件不會被銷燬,那些記憶體不會被釋放。vector的工作原理是系統預先分配一塊CAPACITY大小的空間,當插入的資料超過這個空間的時候,這塊空間會讓某種方式擴充套件,但是你刪除資料的時候,它卻不會縮小。vector為了防止大量分配連續記憶體的開銷,保持一塊預設的尺寸的記憶體,clear只是清資料了未清記憶體,因為vector的 capacity容量未變化,系統維護一個的預設值。

vector與deque(STL容器,雙端佇列)不同,其記憶體佔用空間只會增長,不會減小。比如你首先分配了10,000個位元組,然後erase掉後面9,999個,則雖然有效元素只有一個,但是記憶體佔用仍為10,000個。所有空間在vector析構時回收。empty()是用來檢測容器是否為空的,clear()可以清空所有元素。但是即使clear(),所佔用的記憶體空間依然如故。如果你需要空間動態縮小,可以考慮使用deque。如果非要用vector,這裡有一個辦法:

   在《effective STL》和其實很多C++文章中都有指明,用clear()無法保證記憶體回收。但是swap技法可以。具體方法如下所示:
    vector<int> nums;
   nums.push_back(1);nums.push_back(1);nums.push_back(2);nums.push_back(2);
   
vector<int>().swap(nums);//或者nums.swap(vector<int>());

   vector<int>().swap(nums); 或者如下所示加一對大括號都可以,意思一樣的:  
    {
     std::vector<int> tmp =  nums;   
     nums.swap(tmp);
    }      
    加一對大括號是可以讓tmp退出{}的時候自動析構

   swap技法就是通過交換函式swap(),使得vector離開其自身的作用域,從而強制釋放vector所佔的記憶體空間