噢,是的,对初学者来说Allocator很神秘,希望我这篇文章能带你走进Allocator,看看它到底做了那些事。
1.初识allocator
对大多数人来数使用的IDE是vs.net.那么就看看vs.net这个版本的stl把。"代码面前,了无秘密",<xmemory>文件里,我们可以看到它的实现。
template<class _Ty>
class allocator: public _Allocator_base<_Ty>
{
...
};
class 里先定义了一些类型
typedef _Ty value_type;
typedef value_type _FARQ *pointer;
typedef value_type _FARQ& reference;
typedef const value_type _FARQ *const_pointer;
typedef const value_type _FARQ& const_reference;
typedef _SIZT size_type;
typedef _PDFT difference_type;
接下去是一些借口函数
template<class _Other>
struct rebind
{ // convert an allocator<_Ty> to an allocator <_Other>
typedef allocator<_Other> other;
};
pointer address(reference _Val) const
{ // return address of mutable _Val
return (&_Val);
}
const_pointer address(const_reference _Val) const
{ // return address of nonmutable _Val
return (&_Val);
}
allocator()
{ // construct default allocator (do nothing)
}
allocator(const allocator<_Ty>&)
{ // construct by copying (do nothing)
}
template<class _Other>
allocator(const allocator<_Other>&)
{ // construct from a related allocator (do nothing)
}
template<class _Other>
allocator<_Ty>& operator=(const allocator<_Other>&)
{ // assign from a related allocator (do nothing)
return (*this);
}
void deallocate(pointer _Ptr, size_type)
{ // deallocate object at _Ptr, ignore size
operator delete(_Ptr);
}
pointer allocate(size_type _Count)
{ // allocate array of _Count elements
return (_Allocate(_Count, (pointer)0));
}
pointer allocate(size_type _Count, const void _FARQ *)
{ // allocate array of _Count elements, ignore hint
return (allocate(_Count));
}
void construct(pointer _Ptr, const _Ty& _Val)
{ // construct object at _Ptr with value _Val
_Construct(_Ptr, _Val);
}
void destroy(pointer _Ptr)
{ // destroy object at _Ptr
_Destroy(_Ptr);
}
_SIZT max_size() const
{ // estimate maximum array size
_SIZT _Count = (_SIZT)(-1) / sizeof (_Ty);
return (0 < _Count ? _Count : 1);
}
很清晰,我们只要着重看看这四个函数allocate deallocate construct destroy
申请一个空间遵循这 allocate一个内存空间(内部调用malloc函数)->
constuct在该空间构造一个对象(内部调用new(_ptr)valu_tpye(_Val) )->
destroy 析构对象(内部调用 _Ptr->~_Ty)
deallocate 释放内存(内部调用free);
template<class _Other>
allocator<_Ty>& operator=(const allocator<_Other>&)
{ // assign from a related allocator (do nothing)
return (*this);
}
这个函数很有趣,当我们把一个allocator 赋值给另一个函数时,返回的是自身。也可以理解为什么都不做。如果要是真的把值赋过去了,可能会造成内存泄露。我觉得更好的做法是把operator=作为private,当你不小心调用时,编译器会明确提示你。
接着
// allocator TEMPLATE OPERATORS
template<class _Ty,
class _Other> inline
bool operator==(const allocator<_Ty>&, const allocator<_Other>&)
{ // test for allocator equality (always true)
return (true);
}
template<class _Ty,
class _Other> inline
bool operator!=(const allocator<_Ty>&, const allocator<_Other>&)
{ // test for allocator inequality (always false)
return (false);
}
说明allocator是可以互换的,任何allocator都"相等"。
在接下去特例化了一个allocator<void>
template<> class _CRTIMP2 allocator<void>
{
typedef void _Ty;
typedef _Ty _FARQ *pointer;
typedef const _Ty _FARQ *const_pointer;
typedef _Ty value_type;
....
};
当 value_type _Ty 是void时 ,那么 sizof(void) , void &都是非法操作,所以特化一个,避免错误操作。
OK ,VS.NET 的 std::allocator大体上就是这样子了。vs的stl代码是 P.J.Plauger版本,Dinkumware公司编写。
可以发现,这个版本的allocator内存管理的核心只是简单封装了operator new 和 delete之类的基本操作,并没有什么神秘高明的地方。
2.SGI的allocator
SGI 的allocator相关代码在stl_alloc.h中,代码相对复杂点。
大体上有这么几个类
template<int inst> class __malloc_alloc_template{...}; //一级配置器
template<class _Alloc> class debug_alloc{...}; //for debug
template <bool threads, int inst> class __default_alloc_template {...} //二级配置器
template <class _Tp> class allocator {...} //标准 allocator
template <class _Tp, class _Alloc>struct __allocator {...} //转换接口
SGI默认的allocator是非标准的,但可以转换成标准的。它也提供标准的allocator。SGI allocator最大特色是提供了二级配置器,它在特定条件下启动,在分配小内存是更具效率和减少memory fragment。
3.定制allocator
有时候,为了追求更好的效率和性能,我门需要定制的allocator.现在我们已经对alloctor有了一定的了解了.那么改写一份特别的allocator也就不怎么困难了:
#ifndef _TEST_ALLOCATOR__
#define _TEST_ALLOCATOR__
class pool
{
public:
template<class T>
T* _allocate( ptrdiff_t size)
{
mem_+=size*sizeof(T);
return (T*)mem_;
}
template<class T>
void _deallocate( T*pointer, size_t n )
{
pointer-=n;
mem_ = reinterpret_cast<char*>( pointer);
}
template<class T1,class T2>
void _consturct( T1* p, const T2& val )
{
new( p)T2( val);
}
template<class T>
void _destroy( )
{
;
}
private:
static char* mem_;
};
char buf[50000];
char* pool::mem_ = buf;
template <typename T> class pool_allocator;
template <> class pool_allocator<void>
{
public:
typedef void* pointer;
typedef const void* const_pointer;
// reference to void members are impossible.
typedef void value_type;
template <class U>
struct rebind { typedef pool_allocator<U> other; };
};
template <typename T>
class pool_allocator
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
template <class U>
struct rebind { typedef pool_allocator<U> other; };
pool_allocator(){}
pointer address(reference x) const {retuen &x;}
const_pointer address(const_reference x) const {return &x;}
pointer allocate(size_type size, pool_allocator<void>::const_pointer hint = 0)
{
return ( mem_pool._allocate<T>( (difference_type) size) );
}
//for Dinkumware:
// char *_Charalloc(size_type n){return static_cast<char*>(mem_.allocate(n));}
char* _Charalloc(size_type n)
{
return static_cast<char*> mem_::_allocate((difference_type)n,(pointer)0 );
}
// end Dinkumware
template <class U> pool_allocator(const pool_allocator<U>&){}
void deallocate(pointer p, size_type n)
{
mem_pool._deallocate( p,n);
}
void deallocate(void *p, size_type n)
{
// mem_.deallocate(p, n);
mem_pool._deallocate( (pointer)p,n );
}
size_type max_size() const throw() {return size_t(-1) / sizeof(value_type);}
void construct(pointer p, const T& val)
{
mem_pool._consturct( p, val);
}
void construct(pointer p)
{
new(static_cast<void*>(p)) T();
}
void destroy( pointer p)
{
mem_pool._destroy<T>( );
}
private:
pool mem_pool;
};
//using lis sort need below
template <typename T, typename U>
inline bool operator==(const pool_allocator<T>&, const pool_allocator<U>){return true;}
template <typename T, typename U>
inline bool operator!=(const pool_allocator<T>&, const pool_allocator<U>){return false;}
#endif
//test
vector<int ,pool_allocator<int> > VI;
VI.push_back(1);
VI.push_back(2);
cout<<VI[0]<<" "<<VI[1]<<endl;
VI.pop_back();
VI.pop_back();
cout<<VI.size();
当然这是简单的不能最简单的mem pool,而且可能会造成问题。这里只是抛砖引玉,具体修改class pool即可。