STL(一) allocator 空間配置器

2021-07-12 01:00:22 字數 4177 閱讀 8171

大概…………很久很久以前,我做了乙個浪(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用於在空間分配時候由於記憶體被佔滿了而導致的分配失敗。這裡值得注意的是 這個釋...