1. 程式人生 > >要看STL原始碼,從這裡開始吧

要看STL原始碼,從這裡開始吧

你有沒這樣的經歷,想去看STL原始碼,瀏覽了幾頁後,發現犯困,便又放到了一邊。我也這樣過,但一個偶然的機會,我學習了記憶體池,突然發現STL也有這樣的東西,便再拿起塵封已久的STL原始碼,發現能看懂了。所有,讓我帶你突破STL原始碼的大門吧!

為什麼要使用空間配置器呢?如果你學過C++,那你一定懂得,當需要分配記憶體便使用new和delete,也可以使用malloc和free,不過malloc是不會觸發建構函式的呼叫。記憶體配置器其實也是呼叫這些來分配記憶體,只是它對分配好的記憶體做了進一步的處理才給我們用,讓你程式更高效。下面通過分析STL原始碼解析文件的第二章—空間配置器。幫你開啟STL原始碼的大門,也讓你理解記憶體配置器,大家都叫它記憶體池。

 先說說STL空間配置器的總體概念吧:先分配一個大的記憶體空間(malloc),然後把此塊記憶體分成更小的一塊一塊,程式需要記憶體空間時,不再是使用new或malloc,而是直接從記憶體配置器中拿取一塊空閒的記憶體塊,程式釋放記憶體時,是把記憶體塊放回記憶體配置器。整個過程只使用一次malloc分配,之後的所有操作都是在這大塊記憶體中進行。這樣便更高效,也能減少記憶體碎片。其實這就是記憶體池的思想。後面我會為大家分析一個記憶體池的實現。只要你明白了這個原理,往下看就簡單點了。首先你得下載一個高清的有目錄的STL原始碼文件。

 

從這裡開始,讓你能更好的理解STL空間配置器是怎樣使用上面的原理想法的。STL採用兩級配置器,第一級配置器就是直接向作業系統申請記憶體,看原始碼,可以看到使用的是malloc,沒騙你吧!

第二級配置器就深深地體現了上面所說的記憶體池原理,先分配一塊大記憶體,再把此塊記憶體分成一小塊一小塊。看圖。


上圖他媽畫得太好了。是不是看到那大塊記憶體被分成了許多小塊,並且它們用連結串列組織起來,從連結串列頭取,用完又放回連結串列頭。STL維護了數個這樣的連結串列,基本原理不變。這裡說說,第二級配置器的那塊大記憶體是通過一個函式獲得,其實也是呼叫malloc,只是它在提供給free_list記憶體的時候,多做了很多處理,STL也叫它為記憶體池。說白了,第二級配置器就是連結串列+記憶體池的組合,程式直接從連結串列頭拿記憶體,連結串列沒記憶體了,便想記憶體池申請,記憶體池沒記憶體了就呼叫malloc。

 讀到這裡,你應該比較清楚瞭解記憶體配置器的原理了吧。好吧,現在看看STL提供的記憶體配置函式介面。它提供什麼介面讓我們向它申請記憶體,又用什麼介面給我們釋放呢?

寫得太明顯了,allocate()是申請記憶體,deallocate()是釋放記憶體。似乎在向你呼叫,以後要用記憶體,別再用new或malloc了,用我吧—allocate()。

Allocate()部分內部的實現:

可以看到,分配的記憶體大於128就呼叫第一級配置器,其他都是呼叫二級,大家有沒看到free list的身影。釋放的我就不說了,自己去看。你別以為看了這篇文章就懂了空間配置器,必須的自己去看,去體會,文章只是個引導,讓你下次看STL原始碼不再卡在第二章。

 為了讓大家進一步理解記憶體配置器,下面就為大家提供一段記憶體池的程式碼,你可以拿程式碼去呼叫,也可以拿來使用。我就經常在公司專案中使用。效果剛剛的,其中的Pool.h的記憶體池是我經常使用,裡面有註釋,你看得懂的。具體怎麼用?簡單說說吧

先#include “Pool.h”

Pool  myMem;

//Node *s = new Node();  //註釋的程式碼,原本要使用new的,後來改為記憶體池提供

Node *s = (Node *) myMem.Alloc();   //申請

memset(s,0,sizeof(Node));

new(s)  Node();      //呼叫物件的建構函式

 p->~Node();               //呼叫解構函式

myMem.Free(p);   //釋放

 申請到記憶體後,記得要呼叫建構函式哈,釋放記憶體前記得要呼叫解構函式。這些東西在STL原始碼也有說明:

構造使用construct,析構使用destroy,這兩個東西,到你看vector原始碼便會發現。