C++ STL 容器自定义内存分配器

来源:互联网 发布:js 观察者模式 实例 编辑:程序博客网 时间:2024/05/29 16:56

一,基础篇

很多时候我们不要用默认的allocator的实现,我们需要自己的内存配置,所以我们可以做自己的分配器,这里说说必须要有的一些注意事项,因为有些是我犯错过的。

  1. 需要有自己的一些类型定义比如pointer
  2. 需要做自己的allocate和deallocate
  3. 一定要有rebind实现,如果不理解,请看一下标准库里面的list,set等的实现,很容易的。

 

附上代码:

template <typename T>class MyAlloc : public allocator<T>{public:    typedef size_t   size_type;    typedef typename allocator<T>::pointer              pointer;    typedef typename allocator<T>::value_type           value_type;typedef typename allocator<T>::const_pointer        const_pointer;typedef typename allocator<T>::reference            reference;typedef typename allocator<T>::const_reference      const_reference;    template <typename U>    struct rebind    {        typedef MyAlloc<U> other;    };    pointer allocate(size_type _Count, const void* _Hint = NULL)    {


 4. 做了rebind之后。一定要实现默认构造函数和非同类型一个模板的复制构造函数,最后的代码就如下了:

class MaObjectDisplay1{private:    string DisplayString;public:    MaObjectDisplay1(const char *str)        :DisplayString(str)    {        DisplayString += '\n';    }    void operator() (const int &inObj)    {        printf("inobj %d\n", inObj);    }    bool operator < (const MaObjectDisplay1 & in) const    {        return false;    }};//this alloc class is just for the stl set<> allocatortemplate <typename T>class MyAllc : public allocator<T>{public:    typedef size_t   size_type;    typedef typename allocator<T>::pointer              pointer;    typedef typename allocator<T>::value_type           value_type;typedef typename allocator<T>::const_pointer        const_pointer;typedef typename allocator<T>::reference            reference;typedef typename allocator<T>::const_reference      const_reference;    pointer allocate(size_type _Count, const void* _Hint = NULL)    {        void *rtn = NULL;        //EthCFMMgntRbTreeMem::GetMemInstance()->malloc(_Count, rtn);        return (pointer)rtn;    }    void deallocate(pointer _Ptr, size_type _Count)    {        //EthCFMMgntRbTreeMem::GetMemInstance()->free(_Ptr);    }    template<class _Other>    struct rebind    {// convert this type to allocator<_Other>        typedef MyAllc<_Other> other;    };    MyAllc() throw()     {}     MyAllc(const MyAllc& __a) throw()         : allocator<T>(__a)     {}    template<typename _Tp1>    MyAllc(const MyAllc<_Tp1>&) throw()     {}     ~MyAllc() throw()     {}};int main(){    set<MaObjectDisplay1, less<MaObjectDisplay1 >, MyAllc<MaObjectDisplay1> > myset;    MaObjectDisplay1 a("asdf");    myset.insert(a);}


二、进阶篇 (一)

如果需要内存分配释放有不同的实现,那么,就需要把分配器扩展了,我们可以先试试使用模板参数来提供内存的分配和释放的核心,如下:

#include <stdio.h>#include <set>using namespace std;class M1{public:    static void *getMem(int size)    {        return malloc(size);    }    static void putMem(void *ptr)    {        return free(ptr);    }};class M2{public:    static void *getMem(int size)    {        return malloc(size);    }    static void putMem(void *ptr)    {        return free(ptr);    }};//this alloc class is just for the stl set<> allocatortemplate <typename T, typename M>class MyAllc : public allocator<T>{public:    typedef size_t   size_type;    typedef typename allocator<T>::pointer              pointer;    typedef typename allocator<T>::value_type           value_type;    typedef typename allocator<T>::const_pointer        const_pointer;    typedef typename allocator<T>::reference            reference;    typedef typename allocator<T>::const_reference      const_reference;    pointer allocate(size_type _Count, const void* _Hint = NULL)    {        _Count *= sizeof(value_type);        void *rtn = M::getMem(_Count);        return (pointer)rtn;    }    void deallocate(pointer _Ptr, size_type _Count)    {        M::putMem(_Ptr);    }    template<class _Other>    struct rebind    {   // convert this type to allocator<_Other>        typedef MyAllc<_Other, M> other;    };    MyAllc() throw()    {}    /*MyAllc(const MyAllc& __a) throw()              : allocator<T>(__a)                  {}*/    template<typename _Tp1, typename M1>    MyAllc(const MyAllc<_Tp1, M1>&) throw()    {}    ~MyAllc() throw()    {}};int main(){    set<int, less<int >, MyAllc<int, M1> > set1;    set<int, less<int >, MyAllc<int, M2> > set2;    set1.insert(1);    set2.insert(2);    set1.erase(1);    set2.erase(2);}

这种情况,模板参数是多参了,所以要注意rebind的实现,保证第二个参数不要变,而且,标准库使用rebind的时候,都是单参的,所以我们要提供的依旧是单参的版本,比如看看标准库的使用:

template<class _Ty,class _Alloc0>struct _Tree_base_types{// types needed for a container basetypedef _Alloc0 _Alloc;typedef _Tree_base_types<_Ty, _Alloc> _Myt; #if _HAS_CPP0Xtypedef _Wrap_alloc<_Alloc> _Alty0;typedef typename _Alty0::template rebind<_Ty>::other _Alty; #else /* _HAS_CPP0X */typedef typename _Alloc::template rebind<_Ty>::other _Alty; #endif /* _HAS_CPP0X */typedef typename _Get_voidptr<_Alty, typename _Alty::pointer>::type_Voidptr;typedef _Tree_node<typename _Alty::value_type,_Voidptr> _Node;typedef typename _Alty::template rebind<_Node>::other _Alnod_type;


三、进阶篇 (二)

对于模板多参数,可能有些人不能接受,所以,还有一种办法,对于特定的对象分配,使用不同的allocate版本,就可以对模板类成员函数做一个特化。

class M1{public:    static void *getMem(int size)    {        return malloc(size);    }    static void putMem(void *ptr)    {        return free(ptr);    }};class M2{public:    static void *getMem(int size)    {        return malloc(size);    }    static void putMem(void *ptr)    {        return free(ptr);    }};//this alloc class is just for the stl set<> allocatortemplate <typename T>class MyAllc : public allocator<T>{public:    typedef size_t   size_type;    typedef typename allocator<T>::pointer              pointer;    typedef typename allocator<T>::value_type           value_type;typedef typename allocator<T>::const_pointer        const_pointer;typedef typename allocator<T>::reference            reference;typedef typename allocator<T>::const_reference      const_reference;    pointer allocate(size_type _Count, const void* _Hint = NULL)    {        _Count *= sizeof(value_type);        void *rtn = M1::getMem(_Count);        return (pointer)rtn;    }    void deallocate(pointer _Ptr, size_type _Count)    {        M1::putMem(_Ptr);    }    template<class _Other>    struct rebind    {// convert this type to allocator<_Other>        typedef MyAllc<_Other> other;    };    MyAllc() throw()     {}     /*MyAllc(const MyAllc& __a) throw()         : allocator<T>(__a)     {}*/    template<typename _Tp1>    MyAllc(const MyAllc<_Tp1>&) throw()     {}     ~MyAllc() throw()     {}};template<>MyAllc<double>::pointer MyAllc<double>::allocate(size_type _Count, const void* _Hint){    _Count *= sizeof(value_type);    void *rtn = M2::getMem(_Count);    return (pointer)rtn;}template<>void MyAllc<double>::deallocate(pointer _Ptr, size_type _Count){    M2::putMem(_Ptr);}int main(){    MyAllc<double> aAllc;    aAllc.allocate(1);    aAllc.deallocate(NULL, 1);    set<int, less<int >, MyAllc<int> > set1;    set<double, less<double >, MyAllc<double> > set2;    int a = 1;    double b = 2;    set1.insert(a);    set2.insert(b);    set1.erase(a);    set2.erase(b);}


对于MyAllc<double> aAllc进行的allocate操作,都是使用的特化的,但是后面的set2.insert(b);,根本不会使用特化的内存分配器,为什么呢?呵呵,这个很简单了,set分配内存的单位不是double,而是RBTree_node<double>,所以不适用double特化的分配器。

原创粉丝点击