【STL】实现简单的空间配置器

来源:互联网 发布:九阴绝学玄兵进阶数据 编辑:程序博客网 时间:2024/06/06 00:14

1、模拟实现STL源码中实现的空间配置器

实现方式:
STL源码中的空间配置器的实现分为一级二级空间配置器

1.1 一级空间配置器

所谓的一级空间配置器:(实现特点)
【1】直接使用malloc函数申请内存
【2】自定义清除函数;(当malloc失败之后,调用清除函数来释放一部分的内存空间,以便malloc的成功)
【3】假设调用清除函数之后,还是malloc失败的话,就需要抛出异常 (bad_alloc)
代码实现:
//实现一级空间配置器template<int inst>class  __Malloc_Alloc_Template{public:static  void* allocate(size_t n){void  *  result = malloc(n);//要是malloc失败if(result == NULL){//调用omm_malloc函数result = omm_malloc(n);}return result;}//空间释放static  void  deallocate(void * del){//直接调用freefree(del);}//设置自定义的清理函数 函数返回的是原来的清理函数//函数原型的意思就是 //定义一个函数 返回值是 静态的函数指针参数为空//这个函数的参数为   函数指针参数 为 空 funcstatic void  (*set_malloc_handler(void (* func)()))(){void  (*old)  = ___malloc_alloc_oom_handler;___malloc_alloc_oom_handler = func;return old;}private:static void (*___malloc_alloc_oom_handler)();//表示当前malloc失败内存不足,调用自定义的清理函数static void * omm_malloc(size_t n){void (*my_malloc_handler)();void  *result =NULL;while(1){my_malloc_handler =___malloc_alloc_oom_handler;//表示的当前的清理函数为空的话if(my_malloc_handler == NULL){//直接抛出异常throw bad_alloc();}//调用清理函数my_malloc_handler();//调用完之后重新malloc来申请空间result = malloc(n);if(result){return  result;}}}};template<int inst>void (*__Malloc_Alloc_Template<inst>::___malloc_alloc_oom_handler)()=NULL;

1.2 二级空间配置器

实现方式:
在这里引入内存池与自由链表来实现二级空间配置
二级空间配置器的优缺点:

【内存池】

我们都知道,编译器在用户态与内核态之间的转换是十分的耗时的。
所以,我们可以事先申请一大片的内存空间;将它保存起来,等到需要动态开辟空间的时候,直接到这块空间上切一块,
直到用完之后,重新申请;

【自由链表】

何为自由链表?
就是使用链表将所需要大小的内存块链到一块。
                                       
SGI二級空间配置器保存的自由链表的空间块大小都是8的倍数;并且会將任何小区域的内存块的大小調至8 的倍數(
各自管理大小分別為 8, 16, 24, 32, 40, 48, 56, 64, 72,80, 88, 96, 104, 112, 120, 128 bytes,
当需要的空间大小超过128byte时,直接调用一级空间配置器。
实现代码:
//实现二级空间配置器template<bool threads,int  inst>class __Default_Alloc_Template{public:static void  *  allocate(size_t n){//要是当前要开辟的空间大于128的则直接使用 一级空间配置器来malloc开辟if(n >(size_t) __MAX_BYTES){return __Malloc_Alloc_Template<0>::allocate(n);}//小于128的直接到自由链表中获取//index:得到大小为n的空间要在自由链表的下标为index处获得size_t  index = FREELIST_INDEX(n);//result表示的就是自由链表的第一结点;obj* result = free_list[index];if(result== NULL){//要是当前result 为NULL 表示的当前的自由链表中没有当前大小的内存块//需要从内存池中进行切分return  refill(ROUND_UP(n));}//表示的当前的自由链表中有此大小的内存块;直接拿到;//更新当前的自由链表free_list[index] = result->free_list_link;return  result;}//refill函数表示的是要是当前的自由链表中没有此大小的内存块//需要来从内存池中来切分static void  * refill(size_t n){//表示现在需要到内存池中切分20个这样大小的内存块size_t nobjs= 20;//chunk_alloc为切分函数 char  * chunk = chunk_alloc(n,nobjs);//nobjs表示输出函数 返回的是实际切分到的内存块个数if(nobjs == 1){//要是只切分到一个内存块的话,直接返回就好return  (void *) chunk;}//其他就表示的是得到的内存块的个数大于1//需要将剩余的内存块挂接到的自由链表中size_t  index = FREELIST_INDEX(n);//cur表示的就去除第一个要返回的内存块,的第二个内存块obj *  cur = (obj*)chunk +  n;free_list[index] = cur;for(size_t  i = 2;i < nobjs;++i){obj * next = (obj*)(chunk + i*n);cur->free_list_link = next;cur= next;}cur->free_list_link = NULL;return (void *)chunk;}//chunk_alloc函数来从内存池中切分大小为size的内存块 需要的个数为nobjs//nobjs传引用 ,返回的是实际切分的内存块个数static char * chunk_alloc(size_t size,size_t & nobjs){char * ret  = NULL;size_t total_bytes = size * nobjs;//表示总共需要切分的内存大小size_t bytes_left = end_free - start_free;//表示的是当前的内存池的大小if(bytes_left > total_bytes){//表示的是当前的内存池足够切分当前需要的内存块ret = start_free;start_free += total_bytes;//更新内存池的大小return ret;}else if(bytes_left >=size){//表示的是当前的内存池内不足以开辟当前需要的内存块 //但足够开辟至少一个内存块nobjs = bytes_left /size;//表示的当前切分到的内存块个数ret =start_free ;start_free +=  (nobjs*size);return ret;}else{//表示的是当前的内存池一个内存块都不够切分//则需要重新malloc来开辟空间if(bytes_left != 0)//当前的内存池大小不为0 ,需要处理剩余的内存{//将当前内存池中的空间合理的分配得到自由链表中int index =  FREELIST_INDEX(bytes_left);//头插到到自由链表中((obj*)start_free)->free_list_link = free_list[index];free_list[index] = (obj*)start_free;}//重新开辟新空间size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size>>4);//bytes_to_get表示新开辟空间的大小start_free = (char*)malloc(bytes_to_get);if(start_free == NULL){//表示的是malloc失败,系统已经开不出空间//现在只能从自由链表中来看看for(size_t  i = FREELIST_INDEX(size);i < (size_t)__NFREELISTS;++i ){if(free_list[i] != NULL){start_free = (char*)free_list[i];end_free = start_free + (i+1)*__ALIGN;return  chunk_alloc(size,nobjs);}}end_free =  0 ;start_free = (char*)__Malloc_Alloc_Template<0>::allocate(bytes_to_get);}heap_size = heap_size+ bytes_to_get;end_free = start_free + bytes_to_get;return chunk_alloc(size,nobjs);}}static void  deallocate(void  * p,size_t  n){obj * del =(obj*) p;//要是当前释放的空间大小大于128直接使用一级空间配置器释放if( n >(size_t)__MAX_BYTES ){__Malloc_Alloc_Template<0>::deallocate(p);return  ;}//将释放的空间连到自由链表上size_t index = FREELIST_INDEX(n);del->free_list_link = free_list[index];free_list[index] = del;}private://表示的小型的区域的上调边界enum {__ALIGN = 8};//自由链表的链接的内存的最大值enum {__MAX_BYTES =128};//表示的是自由链表的个数enum {__NFREELISTS = __MAX_BYTES / __ALIGN};//ROUND_UP函数将bytes上调到 8 的倍数static size_t ROUND_UP(size_t  bytes){//任何数+ 7 将这个数 与上 FFF0return (bytes +__ALIGN-1)& ~(__ALIGN-1);}//根据要开辟的空间大小来得到 该大小在自由链表中的下标static size_t  FREELIST_INDEX(size_t bytes){return (bytes + __ALIGN-1)/(__ALIGN-1);}private://链表的结点union obj{union obj *  free_list_link ;//自由链表char client_data[1];};//自由链表数组 ,内部保存一个个的内存块链表static  obj * free_list[__NFREELISTS];static char*  start_free;//内存池的起点static char*  end_free;//内存池的结尾static size_t   heap_size ;//内存池的大小;};template<bool threads,int inst>char  *__Default_Alloc_Template<threads,inst>::start_free = 0;template<bool threads,int inst>char  *__Default_Alloc_Template<threads,inst>::end_free = 0;template<bool threads,int inst>typename __Default_Alloc_Template<threads,inst>::obj*  __Default_Alloc_Template<threads,inst>::free_list[__Default_Alloc_Template<threads,inst>::__NFREELISTS] = {0};template<bool threads,int inst>size_t __Default_Alloc_Template<threads,inst>::heap_size = 0;

2、空间配置器的包装与运用方式


实现:
#ifdef  __USE_MALLOCtypedef __Malloc_Alloc_Template<0> alloc;#elsetypedef  __Default_Alloc_Template<false,0> alloc;#endif //__USE_MALLOCtemplate<class  T ,class  Alloc =alloc>class simple_alloc{public://n表示的是申请T类型的空间的个数static T  * allocate(size_t   n){return 0==n ? NULL : (T *)Alloc::allocate(n*sizeof(T));}//表示申请一个T类型的空间static T *  allocate(void){return  (T*)Alloc::allocate(sizeof(T));}static void deallocate(T *  del){Alloc::deallocate(del,sizeof(T));}static void deallocate(T *  del,size_t  n){if( n!=0)Alloc::deallocate(del,sizeof(T)*n);}};#include<vector>void  Test(){int * array = simple_alloc<int,alloc>::allocate(2);array[0]=0;array[1]=1;cout<<array[0]<<endl;cout<<array[1]<<endl;}


原创粉丝点击