大概…………很久很久以前,我做了乙個浪(er)漫(bi)的決定,就是自己也實現乙個stl,畢竟造輪子呀,才是最浪漫的不是嗎。
於是,山很高,海很深,浪花一浪接一浪,我義無反顧走上了作死之路。
nice ,作死之路的第一部分,就是關於allocator,空間配置器的。
stl 有自己的記憶體管理,其中最最基礎的就是allocator ,任何記憶體的請求和釋放都要經過它。
它本質上就是封裝了一下malloc 和free 函式。
對的,它並不是使用 operator new 和operator delete 來分配和**記憶體,而使用了c 函式的malloc 和free 。事實上,我也很喜歡malloc 和free 函式:)
在接下去閱讀之前,我已經假設,你已經懂得traits 技術(萃取),placement new 、new_handler的應用。如果你對這個一頭霧水,那麼建議你先了解一下或者看看我以往的博文。traits 技術和 new
其次,你在實現的過程中,還可以開啟c++ 的官網,查詢一下標準是怎麼樣的,然後就可以根據標準來實現自己的版本。
為了避免命名衝突,可以把**寫入自己自定義的乙個命名空間中。
在stl 中,allocator 是分為一級和二級的。一級的就處理超過128bytes 的資料,二級的就處理小於128bytes 的資料。但是二級的空間配置器太複雜了,這裡不再詳述,但是強烈建議了解一下。
c++ 的標準是,空間配置器要完成記憶體的分配和構造功能。
但是sgi 中的空間配置器灰常傲嬌。
它的allocator 是使用c函式 malloc ,但是c++ 中的new 是要有記憶體分配和物件構造兩部分組成的。單單malloc 是完成不了乙個 new 的工作量的,那怎麼辦呢?
使用placement new。事實上,stl 中的記憶體分配和物件構造是分開執行的,不同的函式負責執行不同的功能。我覺得很明智,畢竟我不是那麼喜歡new。
sgi 的傲嬌,還體現在命名上,c++ 的空間配置器的標準名稱是allocator ,但是它的叫alloc。
它也做了一些相容的工作,所以,裡面也有符合c++ 標準的版本
這部分的內容在檔案stl_alloc.h
中。
**如下:
typedef
void (*alloc_handler)();
//我也不知道為什麼要設為模板,標準庫就是這麼實現的
//難道是為了以後的擴充套件?
template
class __alloc_template
static
void deallocate(void* p, size_t /* __n */ )
//沒有做證同測試,比較信任realloc 吧
static
void* reallocate(void* p, size_t __n ,size_t __new_size )
static alloc_handler set_alloc_handler ( alloc_handler __new_handler)
};template
alloc_handler __alloc_template::__alloc_oom_handler =0;
template
void* __alloc_template::_xj_oom_malloc(size_t _s)
my_handler();
result = malloc(_s);
if(result) return result;
}}template
void* __alloc_template::_xj_oom_realloc(void*p, size_t _s)
my_handler();
result = realloc(p, _s);
if(result) return result;
}}typedef __alloc_template<0> alloc;
它的做法是,實現乙個模板類先,然後再typedef 一下,就變成廣泛使用的alloc 名稱了。正如注釋所言,我不知道理由在**,唯一能想到的就是為了以後的擴充套件了。
分配記憶體使用allocate 函式
**記憶體使用deallocate 函式。雖然名義上接受兩個引數,但是實際上只需要乙個就可以了,因為free 只要乙個函式。
它還模擬了分配記憶體,空間不足時的new_handler 行為。
__alloc_oom_handler 就是用來儲存處理的控制代碼的,但是除非你指定,不然它是空指標的。
reallocate 就是在已有的空間基礎上重新分配記憶體。
allocate 函式行為很明確,如果請求不到記憶體,那麼就丟給_xj_oom_malloc 來處理。而_xj_oom_malloc 中有乙個無限迴圈,不停地呼叫__alloc_oom_handler 函式(當然這個函式指標必須不為0),試圖做一點最後的處理。
如果__alloc_oom_handler 也無能為力,那麼只要throw 異常。
其中那個巨集實際如下:
#define __xj_throw_bad_alloc throw std::bad_alloc()
這就是alloc 的全部**了。
但是,alloc 並沒有和型別相關,每次分配記憶體的時候,都要額外計算實際的記憶體值。
比如說,我要分配乙個int ,我希望使用的類似於allocate(1)
這樣的,而不是allocate(1*sizeof(int)
。
於是,我們要需要乙個類封裝一下alloc,如下:
/**
* 這是stl使用的,但這個不可以直接去套用
*/templateclass ******_alloc
static t* allocate()
static
void deallocate(t* p, size_t n)
static
void deallocate(t* p)
};
乙個值得注意的問題就是申請的記憶體為0時,怎麼處理。
使用的時候例項化******_alloc 就可以了。
看一下標準的allocator 是怎麼玩的:
/**
* 下面這個是c++ 規定的標準介面,但是sgi 的stl 並不使用這個介面
* 反而使用更簡單一點的,這個可以直接套用
*/template
class allocator
allocator(const allocator& ) {}
template
allocator(const allocator& ) {}
~allocator () {}
//暫時還不明白這個怎麼用
template
struct rebind
;t* address(t& _x)
const t* address(const t& _x)
//__n 不能為0,c++ 沒有說為什麼
//後面那個引數我也不知道是幹嘛的
t* allocate(size_t __n, const
void* = 0)
//p 不能為nullptr
void deallocate(t* p, size_t s)
//size_t (-1) 應該是利用了補碼的表現形式
size_t max_size() const
//construct he destroy
void construct(t* p, const t& value)
void destroy(t* p)
};template
<>
class allocator
;};
這個allocator 在g++ 中,可以直接使用:
vector
v;
**在github 上可以得到。如果中間有變更,不保證部落格的**永遠最新。
二級的空間配置器是處理申請小於128bytes 的記憶體。它使用了記憶體池的概念。
由於比較複雜,這裡不再詳述。
但是,二級配置器的乙個問題在於,小的記憶體碎片永遠不會被真正free ,還給系統。本來就是為了效率而生,怎麼使用倒是看**的需求吧。
STL 空間配置器 allocator《一》
stl的操作物件 所有的數值 都存放在容器之中,而容器則需要配置空間以置放資料。最近在看侯捷的 stl原始碼剖析 所以做了筆記。為什麼不說allocator是記憶體配置器而說他是空間配置器呢?因為空間不一定是記憶體,空間也可以是磁碟或其他輔助儲存介質。一般意義上理解 物件構造的分解 物件記憶體開闢a...
STL相關知識整理 一 allocator
一般而言,我們習慣的 c 記憶體配置操作和釋放操作是這樣的 class foo foo pf new foo delete pf 我們看其中第二行和第三行,雖然都是只有一句,當是都完成了兩個動作。但你 new 乙個物件的時候兩個動作是 先呼叫 operator new 分配乙個物件大小的記憶體,然後...
山寨STL實現之allocator
作為乙個山寨的stl,那麼不得不提的是其中的allocator 空間配置器 顧名思義,它是負責空間分配用的,下面 十分簡單,僅對c函式malloc和free進行了薄薄的一層封裝,同時給定了自定義函式free handler用於在空間分配時候由於記憶體被佔滿了而導致的分配失敗。這裡值得注意的是 這個釋...