1. 程式人生 > 實用技巧 >STL原始碼剖析--空間配置器總結

STL原始碼剖析--空間配置器總結

定義:空間配置器就是分配空間的操作的元件.

設計一個STL的空間配置器有有一些必要的介面,這裡就不一一列舉,列舉兩個在看書中的細節函式

第一個是constuct()負責構造用的.

template <class T1, class T2>
inline void _construct(T1* p, const T2& value){
    new(p) T1(value);
}

這裡是placement new.需要包含標頭檔案#include<new> 相當於p指向的記憶體空間建立一個T1物件,然後把T2強轉給T1,會呼叫T1的建構函式.

優點:相當於把已有空間當成緩衝區,減少分配空間耗費時間,因為直接new去找空閒空間位置比較耗費時間.

第二個是destroy()負責析構用的.

對於destroy有兩個過載版本,一個是對一個destory,則直接呼叫解構函式,另外一個是對一段destory,不會直接呼叫解構函式,而是先取得type_value,

再利用__type_traits<T>判斷此型別是否無關痛癢,若返回__ture_type則說明無關痛癢,什麼也不做,若返回__flase_type則必須呼叫迴圈將一段物件析構.

因為如果範圍很大且都無關痛癢,這樣就節省了時間.

SGI STL中包含兩個空間配置器:

第一個是std::allocator,但這個效率不佳,只是對::operator new和::operator delete做一層薄薄的包裝.

這裡不做詳細說明.

第二個是std::alloc,是SGI STL預設的空間配置器,其中實現採用了記憶體池,對於分配大量小容量的物件,可以大大減少記憶體碎片。

下面做詳細解釋.

std::alloc中又含有有第一級配置器和第二級配置器.預設是第二級配置器,是通過預處理語句#ifdef __USE_MALLOC確定的,__USE_MALLOC未定義.

第一級配置器(__malloc_alloc_template)

第一級配置器是使用malloc(),free(),realloc()等c函式執行實際的記憶體配置,釋放,重配置的操作.也實現了類似於c++中的new-handler機制.

new-handle機制是指,若需求的記憶體配置無法滿足時會不斷地呼叫一個你指定的函式new-handle,當滿足記憶體仍然不滿足需求或者未設定"記憶體不足處理例程"時,

將丟擲bad_alloc異常.

第二級配置器(__default_alloc_template)

若當前區塊大於128bytes時,呼叫第一級,反之呼叫記憶體池管理.這樣的優點是避免了太多小額區塊造成記憶體的碎片,增加配置時的額外負擔.

記憶體池是實現機制是維護16個free-lists,各自管理的大小是其8的倍數,free-lists的實質是連結串列,這個連結串列是由union定義的,這樣做的好處是,不會為了維護連結串列所必須的指標而造成記憶體的浪費.

記憶體池的實現主要介紹有三個函式:

空間配置函式allocate()

當發現大於128則呼叫第一級配置器的allocate,否則看有無可用的free-lists,如果有就用一個類似於連結串列的刪除操作,分配記憶體.因為是從free-lists中拿記憶體分配嘛.否則就呼叫refill().

空間釋放函式deallocate()

當發現大於128則呼叫第一級配置器deallocate,否則就用一個類似於連結串列的插入操作,將區塊回收.

重新填充函式

前面提到的refill()函式呼叫chunk_alloc(),chunk_alloc預設從記憶體池中取出20大小的節點.裡面的機制大概是,如果記憶體池夠,則調出20個區塊,如果不夠,且至少能滿足一個,則返回一個.
如果完全沒有則需要利用malloc從heap中獲取記憶體.如果整個heap也沒有記憶體,則去四處去其他大於當前等級的free-lists找尚未用區塊.找到了就用,否則去第一級配置器找,雖然第一級配置器也是用malloc分配記憶體的但是其中有set-new-handle機制.

最後介紹了五個記憶體處理工具

前面兩個constuct和destory已經講過了.

剩下三個都是對為初始化區域初值填寫,只不過區域的描述不同,都具備"commit or rollback"語意,意思是要麼構造全部,如果有一個失敗則都不構造.

下面是詳細對比:

1.uninitialized_fill_n(first,n,x) first到first+n-1填寫x

先用__type_traits<T1>::is_POD_type判斷是否為POD型別,POD型別是指必然擁有trivialctor/dtor/copy/assignment函式.所以可以用一些高效的初值填寫操作,如果是__true_type,則呼叫fill_n(),否則一一呼叫construct().

2.uninitialized_fill(first,last,x) first到last-1填寫x

先用__type_traits<T1>::is_POD_type判斷是否為POD型別,如果是__true_type,則呼叫fill(),否則一一呼叫construct().

3.uninitialized_copy(first,last,result)將result到result後面last-first-1的賦初值為[first,last)的物件

先用__type_traits<T1>::is_POD_type判斷result是否為POD型別,如果是__true_type,則呼叫copy(),否則一一呼叫construct().

uninitialized_copy針對char*和wchar*有兩個特化版本,採用memmove(直接移動記憶體內容)來執行復制行為,更加高效.

每天進步一點點!!!