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