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

一,基础篇

很多时候我们不要用默认的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<> allocator
template <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<> allocator
template <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 base
	typedef _Alloc0 _Alloc;
	typedef _Tree_base_types<_Ty, _Alloc> _Myt;

 #if _HAS_CPP0X
	typedef _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<> allocator
template <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特化的分配器。

你可能感兴趣的:(C++ STL 容器自定义内存分配器)