眾所周知,一般情況下,一個程序包括數據結構和相應的算法,而數據結構作為存儲數據的組織形式,與內存空間有着密切的聯系. 在C++ STL中,空間配置器便是用來實現內存空間(一般是內存,也可以是硬盤等空間)分配的工具,他與容器聯系緊密,每一種容器的空間分配都是通過空間分配器alloctor實現的.理解alloctor的實現原理,對內存結構以及數據存儲形式會有更清晰直觀的認識.
1.兩種C++類對象實例化方式的異同
在c++中,創建類對象一般分為兩種方式:一種是直接利用構造函數,直接構造類對象,如 Test test();另一種是通過new來實例化一個類對象,如 Test *pTest = new Test;那么,這兩種方式有什么異同點呢?
我們知道,內存分配主要有三種方式:
(1)靜態存儲區分配:內存在程序編譯的時候已經分配好,這塊內存在程序的整個運行空間內都存在.如全局變量,靜態變量等.
(2)棧空間分配:程序在運行期間,函數內的局部變量通過棧空間來分配存儲(函數調用棧),當函數執行完畢返回時,相對應的棧空間被立即回收. 主要是局部變量.
(3)堆空間分配:程序在運行期間,通過在堆空間上為數據分配存儲空間,通過malloc和new創建的對象都是從堆空間分配內存,這類空間需要程序員自己來管理,必須通過free()或者是delete()函數對堆空間進行釋放,否則會造成內存溢出.
那么,從內存空間分配的角度來這兩種方式的區別,就比較容易區分:
對於第一種方式來說,是直接通過調用Test類的構造函數來實例化Test類對象的,如果該實例化對象是一個局部變量,則其是在棧空間分配相應的存儲空間.
對於第二種方式來說,就顯得比較復雜.這里主要以new類對象來說明一下.new一個類對象,其實是執行了兩步操作:首先,調用new在堆空間分配內存,然后調用類的構造函數構造對象的內容;同樣,使用delete釋放時,也是經歷了兩個步驟:首先調用類的析構函數釋放類對象,然后調用delete釋放堆空間.
2.C++ STL空間配置器實現
很容易想象,為了實現空間配置器,完全可以利用new和delete函數並對其進行封裝實現STL的空間配置器,的確可以這樣.但是,為了最大化提升效率,SGI STL版本並沒有簡單的這樣做,而是采取了一定的措施,實現了更加高效復雜的空間分配策略.由於以上的構造都分為兩部分,所以,在SGI STL中,將對象的構造切分開來,分成空間配置和對象構造兩部分.
內存配置操作:alloc::allocate()實現
內存釋放操作:alloc::deallocate()實現
對象構造操作: ::construct()實現
對象釋放操作: ::destroy()實現
STL對象構造與釋放結構圖如下所示:
關於對象的構造和釋放的實現,此處buxijia不細講,但其中利用了replacement new技術,單獨說一下.
replacement new技術實際上就是將一個對象構造在指定的內存區域中.該內存區域可以是靜態數據區,棧空間或者是堆空間,對於內存的管理方式不變.如下代碼說明replacement new的用法.
#include<new.h> Class Test {...}; char *p = (char *)malloc(sizeof(Test)); Test *tp = new(p) Test();//replacement new
關於內存空間的配置與釋放,SGI STL采用了兩級配置器:一級配置器主要是考慮大塊內存空間,利用malloc和free實現;二級配置器主要是考慮小塊內存空間而設計的(為了最大化解決內存碎片問題,進而提升效率),采用鏈表free_list來維護內存池(memory pool),free_list通過union結構實現,空閑的內存塊互相掛接在一塊,內存塊一旦被使用,則被從鏈表中剔除,易於維護.
free_list的結構如下所示:
union obj{ union obj *free_list_link; char client_data[1]; };
free_list的實現技巧如下圖所示:
從free_list取出內存塊示意圖:
釋放內存塊加入free_list示意圖:
以上內容僅供參考學習,歡迎大家交流討論.轉載請注明出處.
參考資料
[1] https://www.cnblogs.com/tony-li/p/4111588.html
[2]STL源碼剖析