空间配置器实现

来源:互联网 发布:php程序员简历 编辑:程序博客网 时间:2024/05/17 21:58

  STL容器可以保存任何C++语言支持的基本类型、用户自定义类型的对象。容器本身只是实现了一种数据结构,对用户数据进行管理。用户数据最终都是保存在内存中的,那么内存的申请、释放工作是由“空间配置器”承担的。标准c++中都提供了std::allocator类。

     当容器中保存的是用户自定义类型数据时,有的数据类型结构简单,占用的空间小;而有的数据类型结构复杂,需要的内存空间大。有的应用程序,需要频繁的进行数据元素的插入、删除操作,这就对应这频繁的内存空间的申请、释放工作。大家都知道频繁的内存操作,会产生严重的性能问题。为了解决这些问题,stlport提供了两个空间配置器。一个是“简单空间配置器”,只是对c运行时库中malloc/free函数进行了简单的封装,唯一不同的是,提供类似new异常处理机制。另一个是“基于内存池的空间配置器”,容器每次申请内存的时候,空间配置器会基于一定的策略,向os申请交大的内存,避免每次内存申请都向os申请。

     下面用图片说明STL具有次级空间配置能力。

根据上图说明,我们可以写出很简陋地STL空间配置器代码:

/*__malloc_alloc_template.h文件** 用来模拟STL中的第一级空间配置器,充分体现了是将申请内存与构造函数分离开来处理,该allocator只负责申请和销毁空间,采用原始maolloc和free函数管理内存,自己动手写异常处理函数*/#ifndef __MALLOC_ALLOC_TEMPLATE_H_INCLUDED#define __MALLOC_ALLOC_TEMPLATE_H_INCLUDED#include<cstdlib>#include<cstddef>namespace juine{    class __malloc_alloc_template    {    //typedef void (*set_alloc_handle)();    private:        typedef void (*handle)();        static handle set_alloc_handle; //异常处理函数指针,由用户制定设定如何解决异常        static void* oom_malloc(size_t n);// 当分配内存失败的时候,调用该函数重新分配内存    public:        static void* alloc(size_t n)        {            void *temp=malloc(n);            temp=0;     //故意的,目的是为了模拟出out of memory时该做些什么            if(!temp)                temp=oom_malloc(n);            return temp;        }        static void dealloc(void* buff,size_t /*n*/)        {            free(buff);        }        static void set_function_handle( void (*temp)())        {            set_alloc_handle=temp;        }        static handle& get_function_handle()        {            return set_alloc_handle;        }    };    void (*__malloc_alloc_template::set_alloc_handle)()=0;//  这个必须写 不然下面函数中将显示set_alloc_handle没有定义    void* __malloc_alloc_template::oom_malloc(size_t n)    {        void *temp=0;        while(!temp)        {            if(set_alloc_handle==0)            {                std::cout<<"you must find a methods to solve the memory problem "<<std::endl;                //抛出异常元素                exit(1);            }            set_alloc_handle();            temp=malloc(n);        }        return temp;    }}#endif // __MALLOC_ALLOC_TEMPLATE_H_INCLUDED


接下来是次级空间配置源码:

/***__default_alloc_template.h文件次级空间配置器*/#ifndef __DEFAULT_ALLOC_TEMPLATE_H_INCLUDED#define __DEFAULT_ALLOC_TEMPLATE_H_INCLUDED#include<cstddef> // for size_t#include<cstdlib>using std::cout;using std::endl;namespace juine{    enum {__ALIGN=8};    enum {__MAX_BYTES=128};    enum {MAXSIZE=__MAX_BYTES/__ALIGN};    union obj    {        obj *next;        char client_data[1];    };    class __default_alloc_template    {    public:        static char *start_pool;   //尽管为静态变量,初始化也应该在内的外面        static char *end_pool;     //为char指针是为了刚好取得1byte内存        static size_t poolSize;        static obj *free_list[MAXSIZE];        //将n转化为8的倍数        static size_t ROUND_UP(size_t n)        {            return (((n)+__ALIGN-1)&~(__ALIGN-1));        }        //获得所在数组的下标        static size_t POSITION(size_t n)        {            return (((n)+__ALIGN-1)/__ALIGN-1);        }        static size_t poolLeftSize()        {            return end_pool-start_pool;        }        //当free_lisk指针为0时,应该从内存池取出内存挂在在指针下面        //,返回给用户的所需的内存地址(刷新指针数组所指的内存分布)        static void* refill(size_t n) //此处n已是8的倍数        {            size_t objs=20;            char *chunk=chunk_alloc(n,objs);            if(objs==1)                return chunk;            obj *result,*current_obj,*next_obj;            result=(obj*)chunk;   //这一块是用来返回给客户端的            //接下来的是将获得的内存节点将其串起来,让以后分配更加方便            free_list[POSITION(n)]=next_obj=(obj*)(chunk+n);            size_t i;            for(i=1;;i++)            {                current_obj=next_obj;                next_obj=(obj*)((char*)current_obj+n);                if(objs-1==i)                {                     current_obj->next=NULL;                     break;                }                current_obj->next=next_obj;            }            std::cout<<"分配客户端后,"<<n<<"bytes大小的块还剩下:"<<i<<std::endl;            return result;        }        //分配内存池空间,然后返回指向数组指针的首地址        static char* chunk_alloc(size_t n,size_t &objs)        {            char *result=NULL;            size_t totalSize=n*objs;            if(poolLeftSize()>totalSize)  //当内存池够大时一般是申请20块            {                result=start_pool;                start_pool+=totalSize;                cout<<"内存空间够大:得到"<<n<<"bytes块20个"<<endl;                return result;    //只是返回首地址而已            }            else if(poolLeftSize()>=n)   //当内存池大于申请内存大小时            {                objs=poolLeftSize()/n;                result=start_pool;                start_pool+=objs*n;                cout<<"内存空间比较大:得到"<<n<<"bytes块"<<objs<<"个"<<endl;                return result;            }            else            {                //需要申请的内存                size_t size_bytes_to_get=objs*n*2+ROUND_UP(poolSize>>4);                std::cout<<"需要申请的内存大小为:"<<size_bytes_to_get<<std::endl;                if(poolLeftSize()>0) //当内存池中还有零头时,将其分配出去                {                    obj *temp;                    temp=free_list[POSITION(poolLeftSize())];  //所剩内存一定是一个块!                    free_list[POSITION(poolLeftSize())]=(obj*)start_pool;                    free_list[POSITION(poolLeftSize())]->next=temp;                    start_pool=end_pool=NULL;                }                start_pool=(char *)malloc(size_bytes_to_get);                end_pool=start_pool+size_bytes_to_get;                result=start_pool;                start_pool+=n*objs;                poolSize+=size_bytes_to_get;                return result;            }        }    public:        static void *alloc(size_t n)   //n为申请的内存大小        {            obj *result=NULL;            //大于128时调用第一级配置器            if(n>(size_t) __MAX_BYTES)            {                return (malloc_alloc::alloc(n));            }            //得到在free_list所在的位子            result=free_list[POSITION(n)];            if(result==NULL)            {                //调用refill,从内存池中来获得内存,将其挂到链表之下                void *r=refill(ROUND_UP(n));                return r;            }            cout<<"指针数组还有内存,已得到指针大小"<<endl;            free_list[POSITION(n)]=result->next;            return result;        }        //释放内存        static void dealloc(void *buff,size_t n)        {            if(n>(size_t) __MAX_BYTES)            {                malloc_alloc::dealloc(buff,n);            }            int pos=POSITION(n);            ((obj*)buff)->next=free_list[pos];            free_list[pos]=(obj*)buff;        }        //无意义,纯粹为了标准接口        typedef void (*handle)();        static handle& get_function_handle()        {            handle p;            return p;        }    };    //初始化数据    char* __default_alloc_template::start_pool=NULL;    char* __default_alloc_template::end_pool=NULL;    size_t __default_alloc_template::poolSize=0;    obj* __default_alloc_template::free_list[MAXSIZE]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};}#endif // __DEFAULT_ALLOC_TEMPLATE_H_INCLUDED

对空间适配器进行统一的封装,对外提供统一的接口

/*    simple_alloc.h**  该类实现对外统一的接口,其中配置器应该使用了单例模式,所有函数,变量都是static变量*/#ifndef SIMPLE_ALLOC_H_INCLUDED#define SIMPLE_ALLOC_H_INCLUDED#include<typeinfo>  //for typeid#include<new>       //for placement new#include"__malloc_alloc_template.h"typedef  juine::__malloc_alloc_template  malloc_alloc;     //注意一下,只能这样写, 顺序很重要#include"__default_alloc_template.h"typedef juine::__default_alloc_template default_alloc;// 指定配置器的类型,选择哪一个来进行配置#ifdef _USE_ALLOC    typedef malloc_alloc my_alloc;#else    typedef default_alloc my_alloc;#endifnamespace juine{    // 该函数用来表示使用第一配置器时,当不给定out of memory处理方法时,则默认使用如下函数(指针)    void fun(){std::cout<<"use default method to solve oom!"<<std::endl;}    void (*temp)()=fun;    template<class T,class Alloc=my_alloc>    class simple_alloc    {    public:    //对ALLOC其进行封装,对外提供统一的接口        T* alloc(size_t n)        {            T* temp=(T*)Alloc::alloc(n);            return temp;        }        T* alloc()        {             T* temp=(T*)Alloc::alloc(sizeof(T));             return temp;        }        void dealloc(T* &buff)        {            Alloc::dealloc(buff,sizeof(T));            buff=NULL;  //释放内存后,指针要归0        }        void dealloc(T* &buff,size_t n)        {            Alloc::dealloc(buff,n*sizeof(T));            buff=NULL;        }        //制定异常处理机制(但是只针对第一种配置情况)        simple_alloc()        {            if(typeid(Alloc)==typeid(malloc_alloc))  //当为次级配置时,为了是不报错,提供了一个无意义的接口                Alloc::get_function_handle()=temp;        }    };    //构造函数工厂    template<class T,class T2>    inline void construct(T *p,T2 value)    {        new(p) T(value);    //placement new 的用法    }    //析构函数工厂    template<class T>    inline void destroy(T *p)  // 为了简单,就只写一个destroy    {        p->~T();    }}#endif // SIMPLE_ALLOC_H_INCLUDED

测试代码如下:

//   test_alloc.cpp#include <iostream>#include<cstring>#include<cstdlib>#define _USE_ALLOC   //用过是否定义这个变量,确定使用的是哪个配置器#include"simple_alloc.h"using namespace std;using namespace juine;struct student{    int id;    string  name;    student(int _id,string _name):id(_id),name(_name){}};ostream& operator<<(ostream &os,const student& ss){    os<<"id:"<<ss.id<<" "<<"name:"<<ss.name;    return os;}int main(void){    simple_alloc<student> ss;    student *p=ss.alloc(3*sizeof(student));    student *q=ss.alloc(3*sizeof(student));    student *t=ss.alloc(3*sizeof(student));    //for(;i<3;i++)           //次级配置中的一个缺陷,其实p指针应该只是指向5个int的,不知到怎么判断越界了没    construct(p,student(1,"小zhang"));   //用来构造对象    construct(p+1,student(2,"小lan"));    construct(p+2,student(3,"小s"));    int i;    for(i=0;i<3;i++)        cout<<p[i]<<endl;    destroy(p);    cout<<p[0]<<endl;}


其测试结果如下:

当时用第一及空间配置器时结果如下:

使用次级配置空间时:


最后结果基本达到预期的想法,实现了空间配置器。

下次,接下来是迭代器的实现!

0 0
原创粉丝点击