一步一步写STL:空间配置器(1)

侯捷说:追踪一流程序,并从中吸取养分,模仿着他写的程序,比那些自以为靠自己努力写出来的下三流程序价值高得多,至少我这么认为——世界上99.999%的程序,在STL面前都是下三流水平!

 

侯捷老师这句话对STL的评价太高了,以前只是熟练使用STL,知道一点原理,受他的影响,最终还是决定研究STL的源码,收益颇丰,还是把这个过程记录下来,并想着借助标准库的原理,自己写一个完整的仿STL,我觉得这是一个很大的工程,因为涉及到高级数据结构,强大的算法,泛型编程思维,STL的掌握,强大的C++编码水平,层次复杂的继承,型别萃取技术,相信这个过程会收益颇丰!毕竟我才大二,时间一大把,我想在我本科期间,做完这一个工程,也无憾了!

 

下图就是STL的层次分布图,当然还缺少一些组件,比如数值处理,pair对组,string,智能指针以及valarray数组等等,其中实现的难点主要集中在几个地方,分别是map红黑树的实现,heap算法体系,函数配接器,流迭代器。尤其是函数配接器,他的内部实现简直是穷尽一切顶尖技巧,令人叹为观止,我先从最左边的内存分配器开始,因为他是所有的核心!

一步一步写STL:空间配置器(1)_第1张图片

 

 

首先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 plain copy print ?
  1. template<class _Ty>  
  2.     struct Allocator_base  
  3.     {   //配置器基类  
  4.     typedef _Ty value_type;  
  5.     };  
  6.   
  7. template<class _Ty>  
  8.     struct Allocator_base<const _Ty>  
  9.     {   //配置器特化于const的基类  
  10.     typedef _Ty value_type;  
  11.     };  
  12.   
  13. template<class _Ty>  
  14. class Allocator  
  15.         :public Allocator_base<_Ty>  
  16. {  
  17. public:  
  18.     //配置器内部型别  
  19.     typedef typename std::size_t size_type;  
  20.     typedef typename std::ptrdiff_t difference_type;  
  21.     typedef _Ty* pointer;  
  22.     typedef const _Ty* const_pointer;  
  23.     typedef _Ty& reference;  
  24.     typedef const _Ty& const_reference;  
  25.     typedef Allocator_base<_Ty> _Mybase;  
  26.     typedef typename _Mybase::value_type value_type;  
  27.   
  28.     //配置器型别转换  
  29.     template<class _other>  
  30.     struct rebind  
  31.     {  
  32.         typedef Allocator<_other> other;  
  33.     };  
  34.   
  35.     //地址函数定义  
  36.     pointer address(reference value)const{  
  37.         return &value;  
  38.     }  
  39.     const_pointer address(const_reference value)const{  
  40.         return (const_pointer)&value:  
  41.     }  
  42.   
  43.     //默认构造函数 什么都不干  
  44.     Allocator() throw() {}  
  45.     //默认复制构造   
  46.     Allocator(const Allocator &)throw() {}  
  47.     //不同配置器的复制构造  
  48.     template<class _otherAll>  
  49.     Allocator(const Allocator<_otherAll> &)throw() {}  
  50.   
  51.     //析构函数  
  52.     ~Allocator()throw() {}  
  53.       
  54.     //返回能分配的最大内存  
  55.     size_type max_size()const throw()  
  56.     {   //借助数值函数  
  57.         numeric_limit<size_type>::max() /sizeof(_Ty);  
  58.     }  
  59.     //分配未构造的内存待用  
  60.     pointer allocate(size_type num,  
  61.         typename Allocator<void>::const_pointer hint= 0)  
  62.     {  
  63.         return (pointer)(::operator new(num * sizeof(_Ty)) );  
  64.     }  
  65.     //在内存中构造对象  
  66.     void construct(pointer p,const_reference value)  
  67.     {  
  68.         new(p) _Ty(value);  
  69.     }  
  70.     //析构内存中的对象  
  71.     void destroy(pointer p)  
  72.     {  
  73.         p->~_Ty();  
  74.     }  
  75.   
  76.     void deallocate( pointer p, size_type size )  
  77.     {  
  78.         ::operator delete(p);  
  79.     }  
  80.     //为了跟标准配置器接轨,这里只能返回true,下一个只能返回false  
  81.     bool operator==(Allocator const& a) const   
  82.     { return true; }  
  83.   
  84.     bool operator!=(Allocator const& a) const   
  85.     { return !operator==(a); }  
  86. };  
  87.   
  88.   
  89. //allocator模版特化于类型void的类  
  90. template<> class Allocator<void>  
  91. {  
  92. public:  
  93.     typedef void _Ty;  
  94.     typedef _Ty* pointer;  
  95.     typedef const _Ty* const_pointer;  
  96.     typedef _Ty value_type;  
  97.   
  98.     template<class _other>  
  99.     struct rebind  
  100.     {  
  101.         typedef Allocator<_other> other;   
  102.     };  
  103.   
  104.     Allocator()throw()   
  105.     { //还是一样,什么都不干  
  106.     }  
  107.   
  108.     Allocator(const Allocator<_Ty>& )throw()  
  109.     { //复制构造,什么都不干  
  110.     }  
  111.   
  112.     template<class _Other>  
  113.         Allocator(const Allocator<_Other>&) throw()  
  114.         {     
  115.         }  
  116.     template<class _Other>  
  117.         Allocator<_Ty>& operator=(const Allocator<_Other>&)  
  118.         {     
  119.         return (*this);  
  120.         }  
  121.   
  122. };  


最开始是两个基类,这两个基类没有任何成员,只有一个内置型别,这两个基类不是必须的,可以略过,只是体现一种好的设计而已,最后一个类是模版特化了一个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 plain copy print ?
  1. #include <iostream>  
  2. #include <list>  
  3. #include <vector>  
  4. #include <algorithm>  
  5. #include "allocator.h"  
  6. using namespace std;  
  7.   
  8. int _tmain(int argc, _TCHAR* argv[])  
  9. {  
  10.     std::vector<double,Allocator<double> > vec_double;  
  11.     std::list<int,Allocator<int> > list_int;  
  12.     for(int i=0;i<60;i++)  
  13.     {  
  14.         list_int.push_back(i);  
  15.         vec_double.push_back( double(i)/3 );  
  16.     }  
  17.       
  18.     list<int,Allocator<int> >::iterator it = list_int.begin();  
  19.     vector<double,Allocator<double> >::iterator io = vec_double.begin();  
  20.   
  21.     cout<<"list test:"<<endl;  
  22.     for(; it!= list_int.end();++it)  
  23.         cout<<*it<<" ";  
  24.     cout<<endl<<endl;  
  25.   
  26.     cout<<"vector test:"<<endl;  
  27.     for(;io!= vec_double.end();++io)  
  28.         cout<<*io<<" ";  
  29.   
  30.     system("pause");  
  31.     return 0;  
  32. }  

一步一步写STL:空间配置器(1)_第2张图片


 

 

 

 

 

 

你可能感兴趣的:(一步一步写STL:空间配置器(1))