STL学习之一:构建自己的内存配置器
使用STL已经有一段时间了,对里面的运作方式一直不了解。这几天突发其想,找了一些关于STL源码的书看了一下,觉得其内部实现非常精妙。作为进一步学习,我打算把STL中的主要组件自己动手实现一下。
首先从空间配置器开始,从内部逐渐了解STL中各种容器的实现细节。
根据STL的规范,allocator必须有以下接口:
typedef size_t size_type;
typedef T value_type;
typedef T * pointer;
typedef ptrdiff_t difference_type;
typedef const T * const_pointer;
typedef T & reference;
typedef const T & const_reference;
rebind
allocator( const allocator < U >& )
~ allocator()
pointer address(reference x)
const_pointer address(const_pointer x)
T * allocate(size_type n, void * p = 0 )
void deallocate(pointer p, size_type n)
size_type max_size()
void construct(pointer p, const_reference val)
void destroy(pointer p)
typedef T value_type;
typedef T * pointer;
typedef ptrdiff_t difference_type;
typedef const T * const_pointer;
typedef T & reference;
typedef const T & const_reference;
rebind
allocator( const allocator < U >& )
~ allocator()
pointer address(reference x)
const_pointer address(const_pointer x)
T * allocate(size_type n, void * p = 0 )
void deallocate(pointer p, size_type n)
size_type max_size()
void construct(pointer p, const_reference val)
void destroy(pointer p)
SGI STL并没有按C++ STD的说没直接做一个Allocator出来,而是先做出两个Allocator template及以内存池的形式来构 //造Allocator比STD::allocator效率更高且减少了内存碎片。下面是sgi_allocator.h的代码:
#ifndef SGI_ALLOCATOR
#define SGI_ALLOCATOR
#include < iostream >
#include < cstddef > // for size_t
using namespace std;
namespace SGI
{
template < int inst >
class malloc_alloc_template // 一级空间配置器
{
public :
static void * allocate(size_t n)
{
void * result = malloc(n);
if (result == NULL)
{
result = oom_malloc(n); // 内存不足调用oom_malloc()进行处理
}
return result;
}
static void deallocate( void * p, size_t) // 第二个参数无所谓,只要一进来就把它干掉..
{
free(p);
}
static void * reallocate( void * p, size_t /**/ /* old size */ , size_t new_size)
{
void * result = realloc(p, new_size);
if (result == NULL)
{
result = oom_realloc(p, new_size);
}
return result;
}
static void ( * set_malloc_handler( void ( * f)()))()
{
void ( * old)() = oom_malloc_handler;
oom_malloc_handler = f;
return old;
}
private :
static void * oom_malloc(size_t); // 内存不足时调用这个函数(因为里面有处理函数)
static void * oom_realloc( void * , size_t); // 同上
static void ( * oom_malloc_handler)(); // 内存不足时的处理函数
} ;
template < int inst >
void * malloc_alloc_template < inst > ::oom_malloc(size_t size)
{
void * result;
while ( true ) // 反得调用处理函数,直到申请成功.如果没有定义处理函数则抛出异常
{
if (oom_malloc_handler == NULL)
{
throw bad_alloc();
}
( * oom_malloc_handler)();
if ( (result = malloc(size)) != NULL)
{
return result;
}
}
}
template < int inst >
void * malloc_alloc_template < inst > ::oom_realloc( void * p, size_t new_size)
{
void * result;
while ( true )
{
if (oom_malloc_handler == NULl)
{
throw bad_alloc();
}
( * oom_alloc_handler)();
if ( (result = realloc(p, new_size)) != NULL)
{
return result;
}
}
}
template < int inst >
void ( * malloc_alloc_template < inst > ::oom_malloc_handler)() = NULL;
typedef malloc_alloc_template < 0 > malloc_alloc; // 到此完成一级空间配置器的定义
/**/ //////////////////////////////////////////////////////
// 下面是对一二级配置器的封装, 其中Alloc即为空间配置器
// 默认用的是二级配置器,当>128K或内存不足时会交给一级
// 配置器处理
/**/ /////////////////////////////////////////////////// //j
template < typename T, typename Alloc >
class simple_alloc
{
public :
static T * allocate(size_t n)
{
return n == 0 ? 0 : (T * )Alloc::allocate(n * sizeof (T));
}
static T * allocate()
{
return (T * )Alloc::allocate( sizeof (T));
}
static void deallocate(T * p, size_t n)
{
if (n != 0 )
{
Alloc::deallocate(p, n * sizeof (T)); // 这里要用两个参数是为了与后面的二级配置器配合(这个问题郁闷了我好久,呵呵,菜啊!)
}
}
static void deallocate(T * p)
{
Alloc::deallocate(p, sizeof (T));
}
} ;
enum {ALIGN = 8 , MAX_BYTES = 128 , NFREELISTS = 16 } ; // 其中NFREELISTS = MAX_BYTES / ALIGN
template < bool threads, int inst >
class default_alloc_template // 定义二级配置器
{
public :
static void * allocate(size_t n)
{
void * result;
if (n > (size_t)MAX_BYTES)
{
result = malloc_alloc::allocate(n); // >128K交给一级配置器处理
}
else
{
Obj * volatile * my_free_list = free_list + free_list_index(n);
if ( (result = * my_free_list) == NULL)
{
result = refill(round_up(n));
}
else
{
Obj * tmp = * my_free_list;
* my_free_list = tmp -> free_list_link;
result = tmp;
}
}
return result;
}
static void deallocate( void * p, size_t n)
{
if (n > (size_t)MAX_BYTES)
{
malloc_alloc::deallocate(p, n);
}
else
{
Obj * volatile * my_free_list = free_list + free_list_index(n);
Obj * q = (Obj * ) p;
q -> free_list_link = * my_free_list;
* my_free_list = q;
}
}
static void * reallocate( void * p, size_t old_size, size_t new_ize);
private :
static size_t round_up(size_t bytes)
{
return ((bytes + (size_t)ALIGN - 1 ) & ~ ((size_t)ALIGN - 1 ));
}
union Obj // Trick,既可以作指针,又可以作为内存地址
{
union Obj * free_list_link;
char client_data[ 1 ];
} ;
static Obj * volatile free_list[NFREELISTS];
static size_t free_list_index(size_t size)
{
return ((size + (size_t)ALIGN - 1 ) / (size_t)ALIGN - 1 );
}
static void * refill(size_t n);
static char * chunk_alloc(size_t size, size_t & objs);
static char * chunk_start; // 内存池起始位置
static char * chunk_end; // 内存池结束位置
static size_t heap_size; // 从开始至今在堆中申请的字节总数
} ;
template < bool threads, int inst >
char * default_alloc_template < threads, inst > ::chunk_start = NULL;
template < bool threads, int inst >
char * default_alloc_template < threads, inst > ::chunk_end = NULL;
template < bool threads, int inst >
size_t default_alloc_template < threads, inst > ::heap_size = 0 ;
template < bool threads, int inst >
typename default_alloc_template < threads, inst > ::Obj * volatile default_alloc_template < threads, inst > ::free_list[NFREELISTS] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
// 核心部分,从内存池中申请空间
template < bool threads, int inst >
char * default_alloc_template < threads, inst > ::chunk_alloc(size_t size, size_t & nobjs)
{
char * result = NULL;
size_t total_bytes = size * nobjs;
size_t bytes_left = chunk_end - chunk_start; // 内存池所剩水量
if ( bytes_left > total_bytes) // 内存池中有足够空间可以分配
{
result = chunk_start;
chunk_start += total_bytes;
return result;
}
else if (bytes_left >= size) // 内存池中空间至少可以分配一个块
{
nobjs = ( int )bytes_left / size;
total_bytes = size * nobjs;
result = chunk_start;
chunk_start += total_bytes;
return result;
}
else // 内存池已山穷水尽一个块都无法分配出来 T_T
{
size_t bytes_to_get = total_bytes + round_up(heap_size >> 4 ); // 申请总数两位加上扩展
if (bytes_left > 0 )
{
Obj * volatile * my_free_list = free_list + free_list_index(bytes_left);
((Obj * )chunk_start) -> free_list_link = * my_free_list;
* my_free_list = (Obj * )chunk_start;
}
chunk_start = ( char * )malloc(bytes_to_get);
if (chunk_start == NULL) // 内存不足这下麻烦了,看看表中有没有空间可以回收
{
Obj * volatile * my_free_list;
Obj * p;
for (size_t i = size; i <= MAX_BYTES; i += ALIGN)
{
my_free_list = free_list + free_list_index(i);
p = * my_free_list;
if (p != NULL) // 找到适合的空间,进行回收
{
* my_free_list = p -> free_list_link;
chunk_start = ( char * )p;
chunk_end += i;
return chunk_alloc(size, nobjs);
}
}
// 完全走投无路了,只好看看内存不足的处理函数能不能帮上忙
chunk_end = NULL;
chunk_start = ( char * )malloc_alloc::allocate(bytes_to_get);
}
chunk_end += bytes_to_get;
heap_size += bytes_to_get;
return chunk_alloc(size, nobjs);
}
}
/**/ /*
* 返回一个大小为n的块, 并且可能适当地为free_list增加节点
*
*/
template < bool threads, int inst >
void * default_alloc_template < threads, inst > ::refill(size_t n)
{
size_t nobjs = 20 ; // 默认申请块的个数
Obj * curr;
Obj * next;
char * result = chunk_alloc(n, nobjs);
if (nobjs == 1 ) // 只申请到一块空间则直接返回给调用者
{
return result;
}
else // 申请到多于一个块,将其它nobjs - 1个块加到free_list中 : )
{
Obj * volatile * my_free_list = free_list + free_list_index(n);;
* my_free_list = next = (Obj * )(result + n);
for ( int i = 1 ; ; ++ i)
{
curr = next;
next = (Obj * )( ( char * )next + n);
if (i == nobjs - 1 )
{
curr -> free_list_link = NULL;
break ;
}
else
{
curr -> free_list_link = next;
}
}
return result;
}
}
template < bool threads, int inst >
static void * default_alloc_template < threads, inst > ::reallocate( void * p, size_t old_size, size_t new_size)
{
if (old_size > MAX_BYTES && new_size > MAX_BYTES)
{
return realloc(p, new_size);
}
if (round_up(old_size) == round_up(new_size))
{
return p;
}
void * result = malloc(new_size);
int copy_size = new_size > old_size ? old_size : new_size;
memcpy(result, p, copy_size);
return result;
}
typedef default_alloc_template < false , 0 > alloc;
/**/ /*
* 对以定义好的两个配置器模板进行封装,以使其与STL相容
*/
template < typename T >
class allocator
{
public :
typedef size_t size_type;
typedef T value_type;
typedef T * pointer;
typedef ptrdiff_t difference_type;
typedef const T * const_pointer;
typedef T & reference;
typedef const T & const_reference;
template < typename U >
struct rebind
{
typedef allocator < U > other;
} ;
allocator() throw ()
{
}
template < typename U >
allocator( const allocator < U >& ) throw ()
{
}
~ allocator() throw ()
{
}
pointer address(reference x) const
{
return & x;
}
const_pointer address(const_pointer x) const
{
return & x;
}
T * allocate(size_type n, void * p = 0 )
{
return n != 0 ? static_cast < T *> (alloc::allocate(n * sizeof (T))) : 0 ;
}
void deallocate(pointer p, size_type n)
{
alloc::deallocate(p, n * sizeof (T));
}
size_type max_size() const throw ()
{
return (size_t) - 1 / sizeof (T);
}
void construct(pointer p, const_reference val)
{
new (p)T(val);
}
void destroy(pointer p)
{
p ->~ T();
}
} ;
}
#endif
#define SGI_ALLOCATOR
#include < iostream >
#include < cstddef > // for size_t
using namespace std;
namespace SGI
{
template < int inst >
class malloc_alloc_template // 一级空间配置器
{
public :
static void * allocate(size_t n)
{
void * result = malloc(n);
if (result == NULL)
{
result = oom_malloc(n); // 内存不足调用oom_malloc()进行处理
}
return result;
}
static void deallocate( void * p, size_t) // 第二个参数无所谓,只要一进来就把它干掉..
{
free(p);
}
static void * reallocate( void * p, size_t /**/ /* old size */ , size_t new_size)
{
void * result = realloc(p, new_size);
if (result == NULL)
{
result = oom_realloc(p, new_size);
}
return result;
}
static void ( * set_malloc_handler( void ( * f)()))()
{
void ( * old)() = oom_malloc_handler;
oom_malloc_handler = f;
return old;
}
private :
static void * oom_malloc(size_t); // 内存不足时调用这个函数(因为里面有处理函数)
static void * oom_realloc( void * , size_t); // 同上
static void ( * oom_malloc_handler)(); // 内存不足时的处理函数
} ;
template < int inst >
void * malloc_alloc_template < inst > ::oom_malloc(size_t size)
{
void * result;
while ( true ) // 反得调用处理函数,直到申请成功.如果没有定义处理函数则抛出异常
{
if (oom_malloc_handler == NULL)
{
throw bad_alloc();
}
( * oom_malloc_handler)();
if ( (result = malloc(size)) != NULL)
{
return result;
}
}
}
template < int inst >
void * malloc_alloc_template < inst > ::oom_realloc( void * p, size_t new_size)
{
void * result;
while ( true )
{
if (oom_malloc_handler == NULl)
{
throw bad_alloc();
}
( * oom_alloc_handler)();
if ( (result = realloc(p, new_size)) != NULL)
{
return result;
}
}
}
template < int inst >
void ( * malloc_alloc_template < inst > ::oom_malloc_handler)() = NULL;
typedef malloc_alloc_template < 0 > malloc_alloc; // 到此完成一级空间配置器的定义
/**/ //////////////////////////////////////////////////////
// 下面是对一二级配置器的封装, 其中Alloc即为空间配置器
// 默认用的是二级配置器,当>128K或内存不足时会交给一级
// 配置器处理
/**/ /////////////////////////////////////////////////// //j
template < typename T, typename Alloc >
class simple_alloc
{
public :
static T * allocate(size_t n)
{
return n == 0 ? 0 : (T * )Alloc::allocate(n * sizeof (T));
}
static T * allocate()
{
return (T * )Alloc::allocate( sizeof (T));
}
static void deallocate(T * p, size_t n)
{
if (n != 0 )
{
Alloc::deallocate(p, n * sizeof (T)); // 这里要用两个参数是为了与后面的二级配置器配合(这个问题郁闷了我好久,呵呵,菜啊!)
}
}
static void deallocate(T * p)
{
Alloc::deallocate(p, sizeof (T));
}
} ;
enum {ALIGN = 8 , MAX_BYTES = 128 , NFREELISTS = 16 } ; // 其中NFREELISTS = MAX_BYTES / ALIGN
template < bool threads, int inst >
class default_alloc_template // 定义二级配置器
{
public :
static void * allocate(size_t n)
{
void * result;
if (n > (size_t)MAX_BYTES)
{
result = malloc_alloc::allocate(n); // >128K交给一级配置器处理
}
else
{
Obj * volatile * my_free_list = free_list + free_list_index(n);
if ( (result = * my_free_list) == NULL)
{
result = refill(round_up(n));
}
else
{
Obj * tmp = * my_free_list;
* my_free_list = tmp -> free_list_link;
result = tmp;
}
}
return result;
}
static void deallocate( void * p, size_t n)
{
if (n > (size_t)MAX_BYTES)
{
malloc_alloc::deallocate(p, n);
}
else
{
Obj * volatile * my_free_list = free_list + free_list_index(n);
Obj * q = (Obj * ) p;
q -> free_list_link = * my_free_list;
* my_free_list = q;
}
}
static void * reallocate( void * p, size_t old_size, size_t new_ize);
private :
static size_t round_up(size_t bytes)
{
return ((bytes + (size_t)ALIGN - 1 ) & ~ ((size_t)ALIGN - 1 ));
}
union Obj // Trick,既可以作指针,又可以作为内存地址
{
union Obj * free_list_link;
char client_data[ 1 ];
} ;
static Obj * volatile free_list[NFREELISTS];
static size_t free_list_index(size_t size)
{
return ((size + (size_t)ALIGN - 1 ) / (size_t)ALIGN - 1 );
}
static void * refill(size_t n);
static char * chunk_alloc(size_t size, size_t & objs);
static char * chunk_start; // 内存池起始位置
static char * chunk_end; // 内存池结束位置
static size_t heap_size; // 从开始至今在堆中申请的字节总数
} ;
template < bool threads, int inst >
char * default_alloc_template < threads, inst > ::chunk_start = NULL;
template < bool threads, int inst >
char * default_alloc_template < threads, inst > ::chunk_end = NULL;
template < bool threads, int inst >
size_t default_alloc_template < threads, inst > ::heap_size = 0 ;
template < bool threads, int inst >
typename default_alloc_template < threads, inst > ::Obj * volatile default_alloc_template < threads, inst > ::free_list[NFREELISTS] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
// 核心部分,从内存池中申请空间
template < bool threads, int inst >
char * default_alloc_template < threads, inst > ::chunk_alloc(size_t size, size_t & nobjs)
{
char * result = NULL;
size_t total_bytes = size * nobjs;
size_t bytes_left = chunk_end - chunk_start; // 内存池所剩水量
if ( bytes_left > total_bytes) // 内存池中有足够空间可以分配
{
result = chunk_start;
chunk_start += total_bytes;
return result;
}
else if (bytes_left >= size) // 内存池中空间至少可以分配一个块
{
nobjs = ( int )bytes_left / size;
total_bytes = size * nobjs;
result = chunk_start;
chunk_start += total_bytes;
return result;
}
else // 内存池已山穷水尽一个块都无法分配出来 T_T
{
size_t bytes_to_get = total_bytes + round_up(heap_size >> 4 ); // 申请总数两位加上扩展
if (bytes_left > 0 )
{
Obj * volatile * my_free_list = free_list + free_list_index(bytes_left);
((Obj * )chunk_start) -> free_list_link = * my_free_list;
* my_free_list = (Obj * )chunk_start;
}
chunk_start = ( char * )malloc(bytes_to_get);
if (chunk_start == NULL) // 内存不足这下麻烦了,看看表中有没有空间可以回收
{
Obj * volatile * my_free_list;
Obj * p;
for (size_t i = size; i <= MAX_BYTES; i += ALIGN)
{
my_free_list = free_list + free_list_index(i);
p = * my_free_list;
if (p != NULL) // 找到适合的空间,进行回收
{
* my_free_list = p -> free_list_link;
chunk_start = ( char * )p;
chunk_end += i;
return chunk_alloc(size, nobjs);
}
}
// 完全走投无路了,只好看看内存不足的处理函数能不能帮上忙
chunk_end = NULL;
chunk_start = ( char * )malloc_alloc::allocate(bytes_to_get);
}
chunk_end += bytes_to_get;
heap_size += bytes_to_get;
return chunk_alloc(size, nobjs);
}
}
/**/ /*
* 返回一个大小为n的块, 并且可能适当地为free_list增加节点
*
*/
template < bool threads, int inst >
void * default_alloc_template < threads, inst > ::refill(size_t n)
{
size_t nobjs = 20 ; // 默认申请块的个数
Obj * curr;
Obj * next;
char * result = chunk_alloc(n, nobjs);
if (nobjs == 1 ) // 只申请到一块空间则直接返回给调用者
{
return result;
}
else // 申请到多于一个块,将其它nobjs - 1个块加到free_list中 : )
{
Obj * volatile * my_free_list = free_list + free_list_index(n);;
* my_free_list = next = (Obj * )(result + n);
for ( int i = 1 ; ; ++ i)
{
curr = next;
next = (Obj * )( ( char * )next + n);
if (i == nobjs - 1 )
{
curr -> free_list_link = NULL;
break ;
}
else
{
curr -> free_list_link = next;
}
}
return result;
}
}
template < bool threads, int inst >
static void * default_alloc_template < threads, inst > ::reallocate( void * p, size_t old_size, size_t new_size)
{
if (old_size > MAX_BYTES && new_size > MAX_BYTES)
{
return realloc(p, new_size);
}
if (round_up(old_size) == round_up(new_size))
{
return p;
}
void * result = malloc(new_size);
int copy_size = new_size > old_size ? old_size : new_size;
memcpy(result, p, copy_size);
return result;
}
typedef default_alloc_template < false , 0 > alloc;
/**/ /*
* 对以定义好的两个配置器模板进行封装,以使其与STL相容
*/
template < typename T >
class allocator
{
public :
typedef size_t size_type;
typedef T value_type;
typedef T * pointer;
typedef ptrdiff_t difference_type;
typedef const T * const_pointer;
typedef T & reference;
typedef const T & const_reference;
template < typename U >
struct rebind
{
typedef allocator < U > other;
} ;
allocator() throw ()
{
}
template < typename U >
allocator( const allocator < U >& ) throw ()
{
}
~ allocator() throw ()
{
}
pointer address(reference x) const
{
return & x;
}
const_pointer address(const_pointer x) const
{
return & x;
}
T * allocate(size_type n, void * p = 0 )
{
return n != 0 ? static_cast < T *> (alloc::allocate(n * sizeof (T))) : 0 ;
}
void deallocate(pointer p, size_type n)
{
alloc::deallocate(p, n * sizeof (T));
}
size_type max_size() const throw ()
{
return (size_t) - 1 / sizeof (T);
}
void construct(pointer p, const_reference val)
{
new (p)T(val);
}
void destroy(pointer p)
{
p ->~ T();
}
} ;
}
#endif
以下是测试文件,现在比较晚了。。没有写一个比较像样的测试,以后有时候再写写。。。
//
sgi_allocator.cpp : 定义控制台应用程序的入口点。
//
/**/ /*
* 模拟SGI STL中allocator的实现,以内存池的形式,构建一个比STD::allocator
* 更高效的空间配置器
* 作者: Szwolf
* 2006.8.3:23.31'暑假@SZU
*
*/
#include " stdafx.h "
#include " sgi_allocator.h "
#include < iostream >
#include < vector >
#include < algorithm >
using namespace std;
class test
{
public :
friend ostream & operator << (ostream & os, const test & x)
{
return os << " test success : ) " << endl;
}
} ;
int _tmain( int argc, _TCHAR * argv[])
{
int a[] = { 1 , 2 , 3 , 4 } ;
test b[ 10 ];
vector < int , SGI::allocator < int > > vec(a, a + 4 );
vector < test, SGI::allocator < test > > vec2(b, b + 10 );
copy(vec.begin(), vec.end(), ostream_iterator < int > (cout, " " ));
cout << endl;
copy(vec2.begin(), vec2.end(), ostream_iterator < test > (cout));
cout << endl;
return 0 ;
}
//
/**/ /*
* 模拟SGI STL中allocator的实现,以内存池的形式,构建一个比STD::allocator
* 更高效的空间配置器
* 作者: Szwolf
* 2006.8.3:23.31'暑假@SZU
*
*/
#include " stdafx.h "
#include " sgi_allocator.h "
#include < iostream >
#include < vector >
#include < algorithm >
using namespace std;
class test
{
public :
friend ostream & operator << (ostream & os, const test & x)
{
return os << " test success : ) " << endl;
}
} ;
int _tmain( int argc, _TCHAR * argv[])
{
int a[] = { 1 , 2 , 3 , 4 } ;
test b[ 10 ];
vector < int , SGI::allocator < int > > vec(a, a + 4 );
vector < test, SGI::allocator < test > > vec2(b, b + 10 );
copy(vec.begin(), vec.end(), ostream_iterator < int > (cout, " " ));
cout << endl;
copy(vec2.begin(), vec2.end(), ostream_iterator < test > (cout));
cout << endl;
return 0 ;
}
一个类似于SGI STL::allocator的空间配置器就这样完成了:)个人感觉在建内存池的过程从其无所不用其极的空间申请方式中受益颇多~~~(呵呵,链表操作又复习了一遍。。)
以下是在VS2005中可以正常编译通过的源码:
http://www.cppblog.com/Files/szwolf/sgi_allocator.rar