Ch2 空间配置器(allocator) ---笔记
2.1 空間配置器的標準接口
allocator的必要接口:
allocator::value_type allocator::pointer allocator::const_pointer allocator::reference allocator::const_reference allocator::size_type allocator::difference_type//一個嵌套的class template(類模板), //class rebind<U>擁有唯一成員other(一個typedef,代表allocator<U>) allocator::rebind//默認的構造函數 allocator::allocator()//copy constructor(復制構造器) allocator::allocator(const allocator&)//泛化的copy constructor template <class U>allocator::allocator(const allocator<U>&)//析構函數 allocator::~allocator()//返回某個對象的地址。算式a.address(x)等同于&x pointer allocator::address(reference x) cosnt//返回某個cosnt對象的地址。算式a.address(x)等同于&x const_pointer allocator::address(const_reference x) const//配置空間,足以存儲n個T對象。 //第二個參數是提示。實現上可能會利用它來增進區域性(locality),或完全忽略之 pointer allocator::allocate(size_type n,const void*=0)//歸還先前配置的空間 void allocator::deallocate(pointer p,size_type n)//返回可成功配置的最大量 size_type allocator::max_size() const//等同于new((void*) p) T(x) void allocator::construct(pointer p,const T& x)//等同于p->~T() void allocator::destroy(pointer p)2.2 具備次配置力(sub-allocation)的SGI空間配置器
SGI STL的配置器名稱是alloc,而非allocator,與標準規范不同,且不接受任何參數。
2.2.1 SGI標準的空間配置器,std::allocator
SGI中也定義有一個符合部分標準,名為allocator的配置器,但該配置器沒有被全面使用,而只是把C++的::operator new和::operator delete做一層薄薄的包裝而已。
2.2.2 SGI特殊的空間配置器,std::alloc
配置器定義于<memory>中,SGI <memory>內包含:
//定義了全局函數construct()和destroy(),負責對象的構造和析構 #include <stl_construct.h>//定義了一、二級配置器,彼此合作。配置器名為alloc。 #include <stl_alloc.h>//定義了一些用來填充(fill)或復制(copy)大塊內存數據的函數, //如:un_initialized_copy() // un_initialized_fill() // un_initialized_fill_n() //這些函數不屬于配置器的范疇,但與對象初值設置有關。 //對于容器的大規模元素初值設置很有幫助 //最差情況下,會調用construct(), //最佳情況下,會使用C標準函數memmove()直接進行內存數據的移動。 #include <stl_uninitialized.h>2.2.3 構造和析構基本工具:construct()和destroy()
/*<stl_construct.h>的部分代碼 */ #include <new.h> //欲使用placement new,需先包含此文件 template <class T1,class T2> inline void construct(T1* p, const T2& value){new(p) T1(value); //placemnt new; 調用T1::T1(value); }//destroy()第一版本,接受一個指針 template <class T> inline void destroy(T* pointer){pointer->~T(); //調用dtor ~T() }//destroy()第二版本,接受兩個迭代器,此函數設法找出元素的數字型別(value type), //進而利用__type_traits<> 求取最適當措施 template <class ForwardIterator> inline void destroy(ForwardIterator first,ForwardIterator last){__destroy(first,last,value_type(first)); }//判斷元素的數值型別(value type)是否有trivial destructor template <class ForwardIterator, class T> inline void __destroy(ForwardIterator first,ForwardIterator last,T*){typedef typename __type_traits<t>::has_trivial_destructor trivial_destructor;__destroy_aux(first,last,trivial_destructor()); }//如果元素的value type有non-trivial destructor template <class ForwardIterator> inline void __destroy_aux(ForwardIterator first,ForwardIterator last,__false_type){for( ;first<last;++first)destroy(&*first); }//如果元素的value type有trivial(無價值的) destructor template <class ForwardIterator> inline void __destroy_aux(ForwardIterator first,ForwardIterator last,__true_type){}//destroy()第二版本針對迭代器為char*和wchar_t*的特化版 inline void destroy(char*,char*){} inline void destroy(wchar_t*,wchar_t*){}2.2.4 空間的配置與釋放,std::alloc
對象構造前的空間配置和對象構造后的空間釋放,由<stl_alloc.h>負責,SGI以malloc()和free()完成內存的配置和釋放。
由于小型區塊可能造成內存碎片問題,所以SGI設計了雙層級配置器:
/*只開放第一級,還是同時開放第二級配置器,取決于__USE_MALLOC是否被定義*/ #ifdef __USE_MALLOC //… typedef __malloc_alloc_template<0> malloc_alloc; typedef malloc_alloc alloc; //令alloc為第一級配置器 #else //… //令alloc為第二級配置器 typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc; #endif /*!__USE_MALLOC *//**SGI STL 第一級配置器*1.allocate()直接使用malloc(),deallocate()直接使用free();*2.模擬C++的set_new_handler()以處理內存不足的狀況。*/ template<int inst> class __malloc_alloc_template { ... }/**SGI STL 第二級配置器*1.維護16個自由鏈表(free lists),負責16中小型區塊的次配置能力,* 內存池(memory pool)以malloc()配置而得;*2.如果需求區塊大于128bytes(內存不足),則轉調用第一級配置器。*/ template <bool threads, int inst> calss __default_alloc_template { ... }SGI還為配置器(無論第一還是第二級)包裝了一個能夠符合STL 規格的接口:
template<class T, class Alloc> class simple_alloc{public:static T *allocate(size_t n){return 0 == n? 0: (T*)Alloc::allocate(n*sizeof(T)); }static T *allocate(void){return (T*)Alloc::allocate(sizeof(T)); }static void deallocate(T *p, size_t n){if(0!=n) Alloc::deallocate(p, n*sizeof(T)); static void deallocate(T *p){Alloc::deallocate(p, sizeof(T)); } };2.2.7 空間配置函數allocate()
__default_alloc_template擁有配置器的標準接口函數allocate()。
//n must be > 0 static void * allocate(size_t n){obj * volatile * my_free_list;obj * result;//大于128就調用第一級配置器if(n > (size_t) __MAX_BYTES){return (malloc_alloc::allocate(n));}//尋找16個free lists中適當的一個my_free_list=free_list+FREELIST_INDEX(n);result=*my_free_list;if(result==0){//沒找到可用的free list,準備重新填充free listvoid *r=refill(ROUND_UP(n));return r;}//調整free list*my_free_list=result->free_list_link;return (result); }?
2.2.8 空間釋放函數deallocate()
__default_alloc_template擁有配置器的標準接口函數deallocate()。
//p 不可以是0 static void deallocate(void *p,size_t n){obj *q=(obj *) p;obj * volatile * my_free_list;//大于128就調用第一級配置器if(n> (size_t) __MAX_BYTES){malloc_alloc::deallocate(p, n);return;}//尋找對應的free listmy_free_list=free_list+FREELIST_INDEX(n);//調整free list,回收區塊q->free_list_link=*my_free_list;*my_free_list=q; }2.2.9 重新填充free lists
當allocate()函數發現free list中沒有可用區塊了時,就調用refill(),準備為free list重新填充空間。
新的空間將取自內存池(經由chunk_alloc()完成)。
缺省取得20個新節點(新區塊),但萬一內存池空間不足,獲得的節點數(區塊數)可能小于20:
//返回一個大小為n的對象,并且有時候會為適當的free list增加節點 //假設n已經適當上調至8的倍數 template <bool threads, int inst> void* __default_alloc_template<threads, inst>::refill(size_t n){int nobjs=20;//調用chunk_alloc(),嘗試取得nobjs個區塊作為free list的新節點//注意參數nobjs是PASS BY REFERENCEchar * chunk=chunk_alloc(n, nobjs);obj * volatile * my_free_list;obj * result;obj * current_obj, *next_obj;int i;//如果只獲得一個區塊,這個區塊就分配給調用者用,free list無新節點if(1 == nobjs) return (chunk);//否則準備調整free list,納入新節點my_free_list=free_list+FREELIST_INDEX(n);//以下在chunk空間內建立free listresult=(obj *)chunk; //這一塊準備返回給客端//以下導引free list指向新配置的空間(取自內存池)*my_free_list=next_obj=(obj *)(chunk+n);//以下將free list的各節點串接起來for( i=1; ; i++){ //從1開始,因為第0個將返回給客端current_obj=next_obj;next_obj=(obj *)((char *)next_obj+n);if(nobjs-1 == i){current_obj->free_list_link=0;break;}else{current_obj->free_list_link=next_obj;}}return (result); }2.2.10 內存池(memory pool)
上一小節提到的chunk_alloc()函數以end_free – start_free來判斷內存池的水量。
如果水量充足,就直接調出20個區塊返回給free list。
如果水量不足以提供20個區塊,但還足夠供應一個(含)以上的區塊,就撥出這不足20個區塊的空間出去。這時候其pass by reference的nobjs參數將被修改為實際能夠供應的區塊數。
如果內存池連一個區塊空間都無法供應,便需利用malloc()從heap中配置內存,為內存池注入源頭活水以應付需求。
新水量的大小為需求量的兩倍,再加上一個隨著配置次數增加而愈來愈大的附加量。
若整個system heap 空間都不夠(以至無法為內存池注入源頭活水),則malloc()失敗,chunk_alloc()就四處尋找有無“尚有為用區塊,且區塊夠大”的free lists。找到了就挖一塊交出,找不到就調用第一級配置器。
第一級配置器其實也是使用malloc()來配置內存,但它有out-of-memory處理機制,或許有機會釋放其他內存拿來此處使用。如果可以,就成功,否則發出bad_alloc異常。
2.3 內存基本處理工具
STL作用于未初始化空間上的五個全局函數——用于構造的construct()、用于析構的destroy(),以及uninitialized_copy(),uninitialized_fill(),uninitialized_fill_n()分別對應高層次函數copy()、fill()、fill_n()。
2.3.1 uninitialized_copy
uninitialized_copy()使我們能夠將內存的配置與對象的構造行為分離開來,
如果作為輸出目的地的[result,result+(last-first)) 范圍內的每一個迭代器都指向未初始化區域,
則uninitialized_copy()會使用copy constructor,給身為輸入來源之[first, last)范圍內的每一個對象產生一份復制品,放進輸出范圍中。
2.3.2 uninitialized_fill
uninitialized_fill()使我們能夠將內存的配置與對象的構造行為分離開來,
如果[first, last)范圍內的每個迭代器都指向未初始化的內存,
那么uninitialized_fill()會在該范圍內產生x(該函數的第三個參數 const T& x)的復制品。
2.3.3 uninitialized_fill_n
uninitialized_fill()使我們能夠將內存的配置與對象的構造行為分離開來。
它會為指定范圍內的所有元素設定相同的初值。
如果[first, first+n)范圍內的每一個迭代器都指向未初始化的內存,
那么uninitialized_fill_n()會調用copy constructor,在該范圍內產生x(該函數的第三個參數 const T& x)的復制品。
這三個函數的進行邏輯是,萃取出迭代器first(uninitialized_copy函數是取出result)的value type,然后判斷該型別是否為POD型別:
—— 如果是POD型別,則執行其對應的高層次函數(分別是copy(),fill(),fill_n());
——如果不是,則只能一個一個元素地構造(遍歷每個迭代器,調用construct()),無法批量進行。
*POD——Plain Old Data,即標量型別(scalar types)或傳統的C struct型別
???????? ——故POD型別必然擁有trivial ctor/tor/copy/assignment函數
???????? ——因此可以對POD型別采用最有效率的初值填寫手法,而對非POD型別只好采用最保險安全的做法。
轉載于:https://www.cnblogs.com/atmacmer/p/6279598.html
總結
以上是生活随笔為你收集整理的Ch2 空间配置器(allocator) ---笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS 处理十六进制颜色渐变算法-输入颜色
- 下一篇: java读写锁