侯捷說:追蹤一流程序,并從中吸取養分,模仿著他寫的程序,比那些自以為靠自己努力寫出來的下三流程序價值高得多,至少我這么認為——世界上99.999%的程序,在STL面前都是下三流水平!
?
侯捷老師這句話對STL的評價太高了,以前只是熟練使用STL,知道一點原理,受他的影響,最終還是決定研究STL的源碼,收益頗豐,還是把這個過程記錄下來,并想著借助標準庫的原理,自己寫一個完整的仿STL,我覺得這是一個很大的工程,因為涉及到高級數據結構,強大的算法,泛型編程思維,STL的掌握,強大的C++編碼水平,層次復雜的繼承,型別萃取技術,相信這個過程會收益頗豐!畢竟我才大二,時間一大把,我想在我本科期間,做完這一個工程,也無憾了!
?
下圖就是STL的層次分布圖,當然還缺少一些組件,比如數值處理,pair對組,string,智能指針以及valarray數組等等,其中實現的難點主要集中在幾個地方,分別是map紅黑樹的實現,heap算法體系,函數配接器,流迭代器。尤其是函數配接器,他的內部實現簡直是窮盡一切頂尖技巧,令人嘆為觀止,我先從最左邊的內存分配器開始,因為他是所有的核心!
?
?
首先STL的內存分配器(空間配置器)在標準庫中充當了異常重要的角色,所有的內存分配,管理,釋放都是由他控制,SGI的設計理念就是把內存管理這一塊紅燈區抽離出來,作為模版參數傳遞進去每個容器,
比如在vector:
template<class T,class Alloc<T> = allocator<T> >
class vector.........
他使用的是內置的默認內存分配器,在上圖中我們看到有兩個分配器,這是SGI STL中的高級之處,實作了多級內存分配,利用內存池實現效率上的優化,同時也減少了內存碎片的可能性。
在這之前需要知道兩個全局函數 ::operator new 和 ::operator delete ,注意不要把他們和一般的new delete混為一談,我們的運算符new在分配內存的時候同時調用對象的構造函數初始化內存,而::operator new只是分配內存,并不調用構造函數,這是實現一塊無初始化內存池的關鍵點,同理delete。
另外還需要了解placement new運算符,他是定位運算符,并不分配內存,只是定位到某一已分配區域!
這里我們先實現一個能跟標準容器接口的分配器類,他并不高明,但是體現了標準分配器的必要特性,其實從某個角度說屬于SGI版本的一級分配器:
[cpp] view plaincopyprint?
template<class?_Ty>??????struct?Allocator_base??????{?????????typedef?_Ty?value_type;??????};????template<class?_Ty>??????struct?Allocator_base<const?_Ty>??????{?????????typedef?_Ty?value_type;??????};????template<class?_Ty>??class?Allocator??????????:public?Allocator_base<_Ty>??{??public:????????????typedef?typename?std::size_t?size_type;??????typedef?typename?std::ptrdiff_t?difference_type;??????typedef?_Ty*?pointer;??????typedef?const?_Ty*?const_pointer;??????typedef?_Ty&?reference;??????typedef?const?_Ty&?const_reference;??????typedef?Allocator_base<_Ty>?_Mybase;??????typedef?typename?_Mybase::value_type?value_type;??????????????template<class?_other>??????struct?rebind??????{??????????typedef?Allocator<_other>?other;??????};??????????????pointer?address(reference?value)const{??????????return?&value;??????}??????const_pointer?address(const_reference?value)const{??????????return?(const_pointer)&value:??????}??????????????Allocator()?throw()?{}????????????Allocator(const?Allocator?&)throw()?{}????????????template<class?_otherAll>??????Allocator(const?Allocator<_otherAll>?&)throw()?{}??????????????~Allocator()throw()?{}??????????????????size_type?max_size()const?throw()??????{?????????????numeric_limit<size_type>::max()?/sizeof(_Ty);??????}????????????pointer?allocate(size_type?num,??????????typename?Allocator<void>::const_pointer?hint=?0)??????{??????????return?(pointer)(::operator?new(num?*?sizeof(_Ty))?);??????}????????????void?construct(pointer?p,const_reference?value)??????{??????????new(p)?_Ty(value);??????}????????????void?destroy(pointer?p)??????{??????????p->~_Ty();??????}????????void?deallocate(?pointer?p,?size_type?size?)??????{??????????::operator?delete(p);??????}????????????bool?operator==(Allocator?const&?a)?const???????{?return?true;?}????????bool?operator!=(Allocator?const&?a)?const???????{?return?!operator==(a);?}??};????????template<>?class?Allocator<void>??{??public:??????typedef?void?_Ty;??????typedef?_Ty*?pointer;??????typedef?const?_Ty*?const_pointer;??????typedef?_Ty?value_type;????????template<class?_other>??????struct?rebind??????{??????????typedef?Allocator<_other>?other;???????};????????Allocator()throw()???????{???????}????????Allocator(const?Allocator<_Ty>&?)throw()??????{???????}????????template<class?_Other>??????????Allocator(const?Allocator<_Other>&)?throw()??????????{?????????????}??????template<class?_Other>??????????Allocator<_Ty>&?operator=(const?Allocator<_Other>&)??????????{?????????????return?(*this);??????????}????};??
template<class _Ty>struct Allocator_base{ //配置器基類typedef _Ty value_type;};template<class _Ty>struct Allocator_base<const _Ty>{ //配置器特化于const的基類typedef _Ty value_type;};template<class _Ty>
class Allocator:public Allocator_base<_Ty>
{
public://配置器內部型別typedef typename std::size_t size_type;typedef typename std::ptrdiff_t difference_type;typedef _Ty* pointer;typedef const _Ty* const_pointer;typedef _Ty& reference;typedef const _Ty& const_reference;typedef Allocator_base<_Ty> _Mybase;typedef typename _Mybase::value_type value_type;//配置器型別轉換template<class _other>struct rebind{typedef Allocator<_other> other;};//地址函數定義pointer address(reference value)const{return &value;}const_pointer address(const_reference value)const{return (const_pointer)&value:}//默認構造函數 什么都不干Allocator() throw() {}//默認復制構造 Allocator(const Allocator &)throw() {}//不同配置器的復制構造template<class _otherAll>Allocator(const Allocator<_otherAll> &)throw() {}//析構函數~Allocator()throw() {}//返回能分配的最大內存size_type max_size()const throw(){ //借助數值函數numeric_limit<size_type>::max() /sizeof(_Ty);}//分配未構造的內存待用pointer allocate(size_type num,typename Allocator<void>::const_pointer hint= 0){return (pointer)(::operator new(num * sizeof(_Ty)) );}//在內存中構造對象void construct(pointer p,const_reference value){new(p) _Ty(value);}//析構內存中的對象void destroy(pointer p){p->~_Ty();}void deallocate( pointer p, size_type size ){::operator delete(p);}//為了跟標準配置器接軌,這里只能返回true,下一個只能返回falsebool operator==(Allocator const& a) const { return true; }bool operator!=(Allocator const& a) const { return !operator==(a); }
};//allocator模版特化于類型void的類
template<> class Allocator<void>
{
public:typedef void _Ty;typedef _Ty* pointer;typedef const _Ty* const_pointer;typedef _Ty value_type;template<class _other>struct rebind{typedef Allocator<_other> other; };Allocator()throw() { //還是一樣,什么都不干}Allocator(const Allocator<_Ty>& )throw(){ //復制構造,什么都不干}template<class _Other>Allocator(const Allocator<_Other>&) throw(){ }template<class _Other>Allocator<_Ty>& operator=(const Allocator<_Other>&){ return (*this);}};
最開始是兩個基類,這兩個基類沒有任何成員,只有一個內置型別,這兩個基類不是必須的,可以略過,只是體現一種好的設計而已,最后一個類是模版特化了一個void類型,這樣做也只是為了使用void的時候不發生未定義行為,從這一點可以看到STL對各種可能的情況都做了充分的預料,我們主要來看Allocator類!
剛開始定義了一對typedef,這是為分配器類定義一堆內置型別,其實也可以不定義啊,只不過在STL中都這樣定義,構建出一種統一的類型型別,方便管理和可讀性
接下來的
template<class _other>
struct rebind
{
???? typedef Allocator<_other> other;
};
這是一個分配器轉換方式,可以方便的轉換為為另一種類型服務的分配器,比如說我們構造的是T類,如果需要構造T*的時候,可以這樣使用
Allocator<T>::rebind<T*>::other? pAllocator;
這樣pAllocator就是一個為T*服務的分配器,具體參考《STL標準程序庫》!
?
該類接下來的函數都是標準接口必須的,不能少任何一個,其中有變動的是這四個 allocate?? deallocate?? destory? construct
allocate函數分配一片連續的未被構造的空間備用,
deallocate??函數釋放空間
construct函數調用布局new,同時調用構造函數,對象被new定位在指定位置
destory?函數調用析構函數,
之所以說這幾個函數可變性比較大,我舉個例子,假如我們做一個學生成績管理系統,當然需要構造學生類,也就是需要從數據庫獲得數據來初始化學生對象,那么你就可以在construct里面內嵌SQL語句,在你盛裝學生對象的容器中,獲得的數據來源不是從鍵盤輸入(參數傳入),而是自動的從數據庫獲取過來,這樣豈不是很方便!同理其他幾個函數,
這個分配器還是很簡單的,就只需要注意那幾點,所以我們在寫自己的分配器希望和標準容器接口時,需要注意這幾點
?
以后還會使用這個分配器,直接拿來當作SGI STL版本的一級分配器,至于二級分配器,下一節在寫!
到此我們可以寫個小程序測試一下了:
[cpp] view plaincopyprint?
#include?<iostream>??#include?<list>??#include?<vector>??#include?<algorithm>??#include?"allocator.h"??using?namespace?std;????int?_tmain(int?argc,?_TCHAR*?argv[])??{??????std::vector<double,Allocator<double>?>?vec_double;??????std::list<int,Allocator<int>?>?list_int;??????for(int?i=0;i<60;i++)??????{??????????list_int.push_back(i);??????????vec_double.push_back(?double(i)/3?);??????}????????????list<int,Allocator<int>?>::iterator?it?=?list_int.begin();??????vector<double,Allocator<double>?>::iterator?io?=?vec_double.begin();????????cout<<"list?test:"<<endl;??????for(;?it!=?list_int.end();++it)??????????cout<<*it<<"?";??????cout<<endl<<endl;????????cout<<"vector?test:"<<endl;??????for(;io!=?vec_double.end();++io)??????????cout<<*io<<"?";????????system("pause");??????return?0;??}??
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include "allocator.h"
using namespace std;int _tmain(int argc, _TCHAR* argv[])
{std::vector<double,Allocator<double> > vec_double;std::list<int,Allocator<int> > list_int;for(int i=0;i<60;i++){list_int.push_back(i);vec_double.push_back( double(i)/3 );}list<int,Allocator<int> >::iterator it = list_int.begin();vector<double,Allocator<double> >::iterator io = vec_double.begin();cout<<"list test:"<<endl;for(; it!= list_int.end();++it)cout<<*it<<" ";cout<<endl<<endl;cout<<"vector test:"<<endl;for(;io!= vec_double.end();++io)cout<<*io<<" ";system("pause");return 0;
}
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的一步一步写STL:空间配置器(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。