1. 程式人生 > >STL——空間配置器

STL——空間配置器

STL有6大元件:容器、演算法、迭代器、仿函式、配接器、分配器。它們之間的密切關係是STL的精髓所在,容器用來存放資料,而容器存在的前提是要有分配器給它分配記憶體,接下來需要實現演算法,迭代器便作為演算法來對容器資料操作的橋樑,演算法可以使用仿函式完成不同的策略變化,配接器可修飾或套接仿函式。

說了麼辣麼多,所有種種始於存在的記憶體,說清分配器的來龍去脈看來是必不可少嘍。本文僅討論SGI STL的空間配置器。

1. 兩級配置器

要說的明白就不能囉嗦,術語定義也不需要,用到了再說。

只需要知道,new一般做了兩件事:分配記憶體與構造物件(delete反之),STL中將這兩個階段分開了!是的,它不再呼叫new來分配記憶體,二是呼叫c式的malloc幹活,這樣使得:記憶體的配置由alloc::allocator()負責(alloc是SGI STL的配置器,其它版本STL採用的配置器是allocator),記憶體釋放由alloc::deallocator()負責,物件的構造和析構由全域性函式::constructor和::destroy負責。兩個分開了!

先看看構造與析構

構造::constuctor接受一個指標p和一個初值value,將初值設定到指標所指的空間上,定義如下:

    //這裡使用的是placement new,需要包含標頭檔案<new.h>
    //為什麼是這樣的寫法,new會開闢新空間,所以不會在想要的地方初始化資料,而這裡的new是placement new,在p所指地址初始化值即可
    template <class T1, class T2>
    inline void constructor(T1 *p, const T2 & value) {
        new (p) T1(value);          
    }

析構::destroy有兩個版本,一個接受指標,可直接析構之;另一個接受first和last迭代器的版本,這時要分兩種情形:判斷元素的數值型別(value type)是否有trival destructor,如果是則什麼都不做,否則就逐個析構,這樣可以提高效率。

問題來了,什麼是value type?什麼又是trival destructor?
暫且這麼來理解,如果元素是int型,根本就不需要呼叫解構函式,所以value type判斷該元素型別是int,其具有trival destructor(沒什麼卵用的析構),那麼不需要逐個呼叫析構函數了,豈不是很清爽!對於複雜物件(要分配空間)的型別,其destructor當然是non_trival。

空間配置和釋放

重要的事情再說一遍:SGI以malloc()和free()完成記憶體配置與釋放。為了簡潔,只說配置,不說釋放。

為了儘量減少小型區塊造成記憶體碎片的問題,STI設計了雙層配置器,這兩級的處理關係如下:
如果定義了__USE_MALLOC則alloc為第一級配置器,否則為第二級。

  1. 第一級配置器會根據需求分配記憶體(malloc()),不夠用時呼叫oom_malloc()(這個會一直嘗試從堆中獲取區塊for(;;))。
  2. 第二級配置器首先判斷所需區塊大小是否大於128bytes,是則呼叫第一級配置器,否轉3。
  3. 從freelist中找(8位元組對齊)對應區塊,有則返回之,並調整freelist,否轉4。
  4. 現在freelist中對應區塊不夠用,那麼呼叫refill函式,預設從memory pool(記憶體池)中chunk_alloc()20個同等大小的新區塊掛到freelist上,不過不夠20個,但夠1個,就拔出這不足20個的區塊給freelist,如果1個都不夠啦,就需要呼叫malloc從heap中配置記憶體,為記憶體池注入活水。

問題又來了:什麼是freelist?
自由連結串列,SGI中定義的是freelist[16],分別指向塊大小為(i+1)*8bytes 的記憶體區,然後相同大小的區塊會形成一個連結串列,有點類似與鄰接表這種資料結構。

2. 記憶體的基本處理工具

STL定義的5個全域性函式:constructor()、destroy()、uninitialized_copy()、uninitialized_fill()、uninitialized_fill_n()。

後三個函式是的記憶體配置與物件構造行為分離開來,copy是將未初始化的輸出去初始化為對應的輸入去資料;fill是對區間內每個迭代器都初始化為給定值x,fill_n將區間起始處後n個迭代器初始化為x。

三者具體實現步驟: 判斷value_type是否為POD, 是則呼叫fill_n(直接賦值),否則呼叫建構函式。

問題來了,什麼是POD?
POD指Plain Old Data,該型別資料擁有trival ctor/dtor/copy/assignment函式,簡單的也可以理解為int。