SGI STL中并没有使用传统的allocator作为空间配置工具,虽然allocator符合STL对于空间配置器的基本要求,但是allocator实质上只是对C++内置的::operator new和::operator delete做了一层包装:
template
inline T * allocate(ptrdiff_t size, T *)
{
set_new_handler(0);
T * tmp = (T *)(::operator new((size_t)(size * sizeof(T))));
if (tmp == 0)
cerr << "out of memmory" << endl;
return temp;
}
SGI STL为了解决这些问题,提供了一个特殊的空间配置器,其思路是首先将配置内存与对象构造分离。SGI STL将空间分配器分装在两个头文件中,一个是
其次是将大块内存的分配与小块内存的分配分开讨论。在这一点上SGI STL选择使用两级处理装置:第一层负责处理大块内存的请求,第二层主要处理小块内存的请求。
那么我们在使用时如何指定使用哪一个内存分配装置呢?
在这一问题上,SGI STL使用了预处理命令:
#ifdef _USE_MALLOC
typedef _malloc_alloc_template<0> malloc_alloc;
typedef malloc_alloc alloc;//第一级配置器
#else
typedef _default_alloc_template<__NODE_ALLOCATOR_THREADS,0> alloc //第二级配置器
#endif
通过预处理命令在第一级或第二级配置器之间选择一个命名为alloc,再为类模板设置第二个模板类型参数:
template
class vector
{
...
};
因为我们将_malloc_alloc_template或_default_alloc_template设置为底层的空间配置器,所以我们还需要设计一个包装函数使其符合常规的空间配置器的使用方式,还可以对底层实现进行更好的封装:
/*
simple_alloc为底层的内存分配类的外部包装,其成员全部调用_malloc_alloc_template
的成员
*/
template
class simple_alloc
{
public:
static T * allocate(void)
{
return (T *)Alloc::allocate(sizeof(T));
}
static T * allocate(size_t n)
{//此allocate接受一个指定对象个数的参数n
return n == 0 ? nullptr : (T *)Alloc::allocate(sizeof(T)*n);
}
static void deallocate(T * p)
{
Alloc::deallocate(p, sizeof(T));
}
static void deallocate(T * p, size_t n)
{
if (n != 0)
Alloc::deallocate(p);
}
};
template
class vector
{
typedef T value_type;
protected:
typedef simple_alloc data_allocator;
...
void deallocate()
{
if (...)
data_allocator::deallocate(start, end_of_storge - start);
}
...
};
第一级配置器的实质是使用了malloc、realloc、free。并处理了内存不足(请求内存失败)的情况,因为::operator new提供了set_new_handler操作,使用户可以自己定制发生内存不足时的处理策略。SGI STL并不能使用C++的set_new_handler,所以单独实现了这个功能。
下面是第一级配置器的实现:
template
class _malloc_alloc_template
{
/* oom_alloc为静态函数成员,用于处理malloc时的内存不足问题
oom_realloc为静态函数成员,用于处理realloc时的内存不足问题
_malloc_alloc_handler为静态数据成员,为void(*)()类型的函数指针,用于用户自
己制定内存分配策略
*/
static void * oom_malloc(size_t);//out_of_memmory malloc
static void * oom_realloc(void *, size_t);
static void(*_malloc_alloc_oom_handler)();
public:
static void * allocate(size_t n)
{
void * result = malloc(n);//请求内存
if (result == nullptr)//如果内存不足
result=oom_malloc(n);//调用oom_malloc
return result;
}
static void * reallocate(void * p, size_t n)
{
void *result = realloc(n);
if (result == nullptr)
result = oom_realloc(p, n);
return result;
}
static void deallocate(void * p)
{
//使用free函数释放p地址后所分配的内存块
free(p);
}
/*此静态成员函数接受一个void(*)()类型的函数指针作为参数,返回
void(*)()类型的函数指针。其作用为用用户自己定制的内存调度方法替换
_malloc_alloc_handler,由此实现类似C++的set_new_handler方法。
*/
static void(* set_malloc_handler(void(*f)()))()
{
void(*old)() = _malloc_alloc_oom_handler;
_malloc_alloc_oom_handler = f;
return old;
}
};
template
void(*_malloc_alloc_template::_malloc_alloc_oom_handler)() = 0;
template
void * _malloc_alloc_template::oom_malloc(size_t n)
{
void(*my_oom_handler)();
void * result;
//无限循环,直至成功分配内存或用户没有定制内存分配策略
for (;;)
{
my_oom_handler = _malloc_alloc_oom_handler;
if (my_oom_handler == nullptr)//如果用户没有定制内存分配策略
exit(1);
(*my_oom_handler)();//使用用户定制的方法
result = malloc(n);
if (result)
return result;
}
}
template
void * _malloc_alloc_template::oom_realloc(void * p, size_t n)
{
//此函数的设计思路与oom_malloc如出一辙
void(*my_oom_handler)();
void * result;
for (;;)
{
my_oom_handler = _malloc_alloc_oom_handler;
if (my_oom_handler == nullptr)
exit(1);
(*my_oom_handler)();
result = realloc(p,n);
if (result)
return result;
}
}
/*本文部分内容引用《STL源码剖析》侯捷著*/