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

一,基础篇

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

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

附上代码:

  1. template <typename T>  
  2. class MyAlloc : public allocator<T>  
  3. {  
  4. public:  
  5.     typedef size_t   size_type;  
  6.     typedef typename allocator<T>::pointer              pointer;  
  7.     typedef typename allocator<T>::value_type           value_type;  
  8.     typedef typename allocator<T>::const_pointer        const_pointer;  
  9.     typedef typename allocator<T>::reference            reference;  
  10.     typedef typename allocator<T>::const_reference      const_reference;  
  11.   
  12.     template <typename U>  
  13.     struct rebind  
  14.     {  
  15.         typedef MyAlloc<U> other;  
  16.     };  
  17.   
  18.     pointer allocate(size_type _Count, const void* _Hint = NULL)  
  19.     {  


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

  1. class MaObjectDisplay1  
  2. {  
  3. private:  
  4.     string DisplayString;  
  5. public:  
  6.     MaObjectDisplay1(const char *str)  
  7.         :DisplayString(str)  
  8.     {  
  9.         DisplayString += '\n';  
  10.     }  
  11.   
  12.     void operator() (const int &inObj)  
  13.     {  
  14.         printf("inobj %d\n", inObj);  
  15.     }  
  16.   
  17.     bool operator < (const MaObjectDisplay1 & in) const  
  18.     {  
  19.         return false;  
  20.     }  
  21. };  
  22.   
  23. //this alloc class is just for the stl set<> allocator  
  24. template <typename T>  
  25. class MyAllc : public allocator<T>  
  26. {  
  27. public:  
  28.     typedef size_t   size_type;  
  29.     typedef typename allocator<T>::pointer              pointer;  
  30.     typedef typename allocator<T>::value_type           value_type;  
  31.     typedef typename allocator<T>::const_pointer        const_pointer;  
  32.     typedef typename allocator<T>::reference            reference;  
  33.     typedef typename allocator<T>::const_reference      const_reference;  
  34.   
  35.     pointer allocate(size_type _Count, const void* _Hint = NULL)  
  36.     {  
  37.         void *rtn = NULL;  
  38.         //EthCFMMgntRbTreeMem::GetMemInstance()->malloc(_Count, rtn);  
  39.         return (pointer)rtn;  
  40.     }  
  41.   
  42.     void deallocate(pointer _Ptr, size_type _Count)  
  43.     {  
  44.         //EthCFMMgntRbTreeMem::GetMemInstance()->free(_Ptr);  
  45.     }  
  46.   
  47.     template<class _Other>  
  48.     struct rebind  
  49.     {   // convert this type to allocator<_Other>  
  50.         typedef MyAllc<_Other> other;  
  51.     };  
  52.   
  53.     MyAllc() throw()   
  54.     {}   
  55.   
  56.     MyAllc(const MyAllc& __a) throw()   
  57.         : allocator<T>(__a)   
  58.     {}  
  59.   
  60.     template<typename _Tp1>  
  61.     MyAllc(const MyAllc<_Tp1>&) throw()   
  62.     {}   
  63.   
  64.     ~MyAllc() throw()   
  65.     {}  
  66. };  
  67.   
  68.   
  69. int main()  
  70. {  
  71.     set<MaObjectDisplay1, less<MaObjectDisplay1 >, MyAllc<MaObjectDisplay1> > myset;  
  72.   
  73.     MaObjectDisplay1 a("asdf");  
  74.     myset.insert(a);  
  75. }  


二、进阶篇 (一)

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

  1. #include <stdio.h>  
  2.   
  3. #include <set>  
  4.   
  5. using namespace std;  
  6.   
  7.   
  8. class M1  
  9. {  
  10. public:  
  11.     static void *getMem(int size)  
  12.     {  
  13.         return malloc(size);  
  14.     }  
  15.   
  16.     static void putMem(void *ptr)  
  17.     {  
  18.         return free(ptr);  
  19.     }  
  20. };  
  21.   
  22. class M2  
  23. {  
  24. public:  
  25.     static void *getMem(int size)  
  26.     {  
  27.         return malloc(size);  
  28.     }  
  29.   
  30.     static void putMem(void *ptr)  
  31.     {  
  32.         return free(ptr);  
  33.     }  
  34. };  
  35.   
  36. //this alloc class is just for the stl set<> allocator  
  37. template <typename T, typename M>  
  38. class MyAllc : public allocator<T>  
  39. {  
  40. public:  
  41.     typedef size_t   size_type;  
  42.     typedef typename allocator<T>::pointer              pointer;  
  43.     typedef typename allocator<T>::value_type           value_type;  
  44.     typedef typename allocator<T>::const_pointer        const_pointer;  
  45.     typedef typename allocator<T>::reference            reference;  
  46.     typedef typename allocator<T>::const_reference      const_reference;  
  47.   
  48.     pointer allocate(size_type _Count, const void* _Hint = NULL)  
  49.     {  
  50.         _Count *= sizeof(value_type);  
  51.         void *rtn = M::getMem(_Count);  
  52.   
  53.         return (pointer)rtn;  
  54.     }  
  55.   
  56.     void deallocate(pointer _Ptr, size_type _Count)  
  57.     {  
  58.         M::putMem(_Ptr);  
  59.     }  
  60.   
  61.     template<class _Other>  
  62.     struct rebind  
  63.     {   // convert this type to allocator<_Other>  
  64.         typedef MyAllc<_Other, M> other;  
  65.     };  
  66.   
  67.     MyAllc() throw()  
  68.     {}  
  69.   
  70.     /*MyAllc(const MyAllc& __a) throw() 
  71.               : allocator<T>(__a) 
  72.                   {}*/  
  73.   
  74.     template<typename _Tp1, typename M1>  
  75.     MyAllc(const MyAllc<_Tp1, M1>&) throw()  
  76.     {}  
  77.   
  78.     ~MyAllc() throw()  
  79.     {}  
  80. };  
  81.   
  82.   
  83. int main()  
  84. {  
  85.     set<int, less<int >, MyAllc<int, M1> > set1;  
  86.     set<int, less<int >, MyAllc<int, M2> > set2;  
  87.   
  88.     set1.insert(1);  
  89.     set2.insert(2);  
  90.   
  91.     set1.erase(1);  
  92.     set2.erase(2);  
  93. }  

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

  1. template<class _Ty,  
  2.     class _Alloc0>  
  3.     struct _Tree_base_types  
  4.     {   // types needed for a container base  
  5.     typedef _Alloc0 _Alloc;  
  6.     typedef _Tree_base_types<_Ty, _Alloc> _Myt;  
  7.   
  8.  #if _HAS_CPP0X  
  9.     typedef _Wrap_alloc<_Alloc> _Alty0;  
  10.     typedef typename _Alty0::template rebind<_Ty>::other _Alty;  
  11.   
  12.  #else /* _HAS_CPP0X */  
  13.     typedef typename _Alloc::template rebind<_Ty>::other _Alty;  
  14.  #endif /* _HAS_CPP0X */  
  15.   
  16.     typedef typename _Get_voidptr<_Alty, typename _Alty::pointer>::type  
  17.         _Voidptr;  
  18.     typedef _Tree_node<typename _Alty::value_type,  
  19.         _Voidptr> _Node;  
  20.   
  21.     typedef typename _Alty::template rebind<_Node>::other _Alnod_type;  


三、进阶篇 (二)

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

  1. class M1  
  2. {  
  3. public:  
  4.     static void *getMem(int size)  
  5.     {  
  6.         return malloc(size);  
  7.     }  
  8.   
  9.     static void putMem(void *ptr)  
  10.     {  
  11.         return free(ptr);  
  12.     }  
  13. };  
  14.   
  15. class M2  
  16. {  
  17. public:  
  18.     static void *getMem(int size)  
  19.     {  
  20.         return malloc(size);  
  21.     }  
  22.   
  23.     static void putMem(void *ptr)  
  24.     {  
  25.         return free(ptr);  
  26.     }  
  27. };  
  28.   
  29. //this alloc class is just for the stl set<> allocator  
  30. template <typename T>  
  31. class MyAllc : public allocator<T>  
  32. {  
  33. public:  
  34.     typedef size_t   size_type;  
  35.     typedef typename allocator<T>::pointer              pointer;  
  36.     typedef typename allocator<T>::value_type           value_type;  
  37.     typedef typename allocator<T>::const_pointer        const_pointer;  
  38.     typedef typename allocator<T>::reference            reference;  
  39.     typedef typename allocator<T>::const_reference      const_reference;  
  40.   
  41.     pointer allocate(size_type _Count, const void* _Hint = NULL)  
  42.     {  
  43.         _Count *= sizeof(value_type);  
  44.         void *rtn = M1::getMem(_Count);  
  45.   
  46.         return (pointer)rtn;  
  47.     }  
  48.   
  49.     void deallocate(pointer _Ptr, size_type _Count)  
  50.     {  
  51.         M1::putMem(_Ptr);  
  52.     }  
  53.   
  54.     template<class _Other>  
  55.     struct rebind  
  56.     {   // convert this type to allocator<_Other>  
  57.         typedef MyAllc<_Other> other;  
  58.     };  
  59.   
  60.     MyAllc() throw()   
  61.     {}   
  62.   
  63.     /*MyAllc(const MyAllc& __a) throw()  
  64.         : allocator<T>(__a)  
  65.     {}*/  
  66.   
  67.     template<typename _Tp1>  
  68.     MyAllc(const MyAllc<_Tp1>&) throw()   
  69.     {}   
  70.   
  71.     ~MyAllc() throw()   
  72.     {}  
  73. };  
  74.   
  75. template<>  
  76. MyAllc<double>::pointer MyAllc<double>::allocate(size_type _Count, const void* _Hint)  
  77. {  
  78.     _Count *= sizeof(value_type);  
  79.     void *rtn = M2::getMem(_Count);  
  80.   
  81.     return (pointer)rtn;  
  82. }  
  83. template<>  
  84. void MyAllc<double>::deallocate(pointer _Ptr, size_type _Count)  
  85. {  
  86.     M2::putMem(_Ptr);  
  87. }  
  88.   
  89.   
  90. int main()  
  91. {  
  92.     MyAllc<double> aAllc;  
  93.     aAllc.allocate(1);  
  94.     aAllc.deallocate(NULL, 1);  
  95.     set<int, less<int >, MyAllc<int> > set1;  
  96.     set<double, less<double >, MyAllc<double> > set2;  
  97.   
  98.     int a = 1;  
  99.     double b = 2;  
  100.     set1.insert(a);  
  101.     set2.insert(b);  
  102.   
  103.     set1.erase(a);  
  104.     set2.erase(b);  
  105. }  


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

猜你喜欢

转载自blog.csdn.net/sunweiliang/article/details/81514818
今日推荐