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

空間配置器——allocator

主要介紹一下allocator的用法

一、為什麼要有allocator

為什麼會有allocator?原因是new在記憶體分配上面有一些侷限性,new的機制是將記憶體分配和物件構造組合在一起,同樣的,delete也是將物件析構和記憶體釋放組合在一起。但當分配一塊大塊記憶體時,我們想要自己在這塊記憶體上構建物件,將分配空間和構造物件分離,就要用到allocator

 C++的STL中定義了很多容器,容器的第二個模板引數通常為allocator型別且有一個預設的allocator。標準庫中allocator類定義在標頭檔案memory中,用於幫助將記憶體分配和物件的構造分離開來。它分配的記憶體是原始的、未構造的。和vector等一樣,allocator也是一個模板類,為了定義一個allocator物件,我們需指明這個allocator可以分配的物件型別,這樣allocator好根據給定的物件型別來確定合適的記憶體空間大小和對齊位置,例:

allocator<string> alloc;  //定義了一個可以分配string的allocator物件
auto const p=alloc.allocate(n);  //分配n個未初始化的string記憶體,即為n個空string分配了記憶體

二、allocator用法概述

常見操作總結如下:

allocator<T> a         

  •  定義了一個名為a的allocator物件,它可以為型別T的物件分配記憶體

a.allocate(n)           

  • 分配一段原始的、未構造的記憶體,這段記憶體能儲存n個型別為T的物件

a.deallocate(p,n)    

  • 釋放T*指標p地址開始的記憶體,這塊記憶體儲存了n個型別為T的物件,p必須是一個先前由allocate返回的指標,且n必須是p建立時所要求的大小,且在呼叫該函式之前必須銷燬在這片記憶體上建立的物件。要求還蠻多的哈,這是因為在建立過程中我們分配的是最原始的記憶體,所以在釋放記憶體時也是隻能嚴格釋放這片最原始的記憶體。

a.construct(p,args)

  • p必須是一個型別為T* 的指標,指向一片原始記憶體,arg將被傳遞給型別為T的建構函式,用來在p指向的原始記憶體上構建物件。

a.destory(p)  

  • p為T*型別的指標,用於對p指向的物件執行解構函式

三、詳情

1.allocate用於分配原始記憶體
construct成員函式接受一個指標和零個或多個額外的引數,在給定位置構造物件,額外的引數是用於初始化構造物件的。

auto q=p;  //q指向最後構造的元素之後的位置
alloc.construct(q++);   //*q為空字串
alloc.construct(q++,10,'c');  //*q為cccccccccc
alloc.construct(q++,"hi");  //*q為hi
用完物件後,必須對這種構造的的物件呼叫destory銷燬,它接受一個指標,對指向的物件執行解構函式。
while(q!=p)
    alloc.destory(--q);

迴圈開始處,q是指向最後構造的元素之後的一個位置,呼叫destory之前我們先對q進行遞減操作,所以第一次呼叫destory銷燬的是最後一個元素,依次執行銷燬操作直到q和p相等。我們只能對真正構造了的元素進行destory操作。一旦元素被銷燬,就可以重新使用這部分記憶體來儲存其他string或歸還給系統,釋放記憶體通過呼叫deallocate完成。

alloc.deallocate(p,n);
其中p不能為空,必須指向allocate分配的記憶體,而且大小引數n也必須與呼叫allocated分配記憶體時提供的大小引數相等。

三、拷貝和填充未初始化記憶體的演算法

allocator還有兩個伴隨演算法,用於在未初始化的記憶體塊中建立物件,這些函式在給定目的位置建立元素,而不是由系統分配記憶體給他們,同樣它們也位於標頭檔案memory中。

uninitialized_copy(b,e,b2)      

  •  從迭代器b和e指出的輸入範圍中拷貝元素到迭代器b2指定的未構造的原始記憶體中,b2指向的記憶體必須足夠大,能容納輸入序列中元素的拷貝。

uninitialized_copy_n(b,n,b2)  

  •  從迭代器b指向的元素開始,拷貝n個元素到b2開始的記憶體中

uninitialized_fill(b,e,t)              

  •  在迭代器b和e指定的原始記憶體範圍中建立物件,物件的值均為t的拷貝

uninitalized_fiil_n(b,n,t)            

  •  在迭代器b指向的記憶體地址開始建立n個物件,b必須指向足夠大的未構造的原始記憶體,能夠容乃給定數量的物件

以上函式將返回一個迭代器,指向最後一個構造的元素之後的位置。

假定有一個int的vector,希望將它的內容拷貝到動態記憶體中,我們將分配一塊比vector中元素所佔空間大一倍的動態記憶體,然後將原vector中的元素拷貝到前一半空間,後一半用一個給定值進行填充。

//分配比vi向量所佔空間大一倍的動態記憶體
auto p=alloc.allocate(vi.size()*2);
//通過拷貝vi中的元素來構造從p開始的元素
auto q=uninitialized_copy(vi.begin(),vi.end(),p);
//將剩餘元素初始化為42
uninitialized_fill_n(q,vi.size(),42);

來源:C++ primer