虽然STL的内存配置器在我们的实际应用中几乎不用涉及,但它却在STL的各种容器背后默默做了大量的工作,STL内存配置器为容器分配并管理内存。统一 的内存管理使得STL库的可用性、可移植行、以及效率都有了很大的提升,因此STL内存配置器是STL库中最重要的组成部分之一。
1、Constructor和Destroy
我们所习惯的C++内存空间分配动作如下:
- class Foo { ... };
- Foo* pObj = new Foo;
- delete pObj;
其中new操作符的动作可以分为两部分:1. 为对象分配内存空间(
alloc::allocate 函数);2. 调用对象的构造函数(
alloc::construct 函数)
delete操作符的动作也可以分为两部分:1. 调用对象的析构函数 (alloc::destroy 函数);2. 释放对象占用的内存空间(alloc::deallocate 函数)
在STL库的空间配置器中,new和delete的这两个过程都是分开的,这还要归功于C++语言的placement new操作符(具体请参考C++Primer)。
construct源码
- template <class _T1,
class _T2>
- inline void _Construct(_T1* __p,
const _T2& __value) {
- new ((void*) __p) _T1(__value);
- }
- template <class _T1>
- inline void _Construct(_T1* __p) {
- new ((void*) __p) _T1();
- }
- template <class _T1,
class _T2>
- inline void construct(_T1* __p,
const _T2& __value) {
- _Construct(__p, __value);
- }
- template <class _T1>
- inline void construct(_T1* __p) {
- _Construct(__p);
- }
destroy源码
- template <class _Tp>
- inline void _Destroy(_Tp* __pointer) {
- __pointer->~_Tp();
- }
- template <class _ForwardIterator>
- inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) {
- __destroy(__first, __last, value_type(__first));
- }
- inline void _Destroy(char*,
char*) {}
- inline void _Destroy(int*,
int*) {}
- inline void _Destroy(long*,
long*) {}
- inline void _Destroy(float*,
float*) {}
- inline void _Destroy(double*,
double*) {}
- #ifdef __STL_HAS_WCHAR_T
- inline void _Destroy(wchar_t*,
wchar_t*) {}
- #endif /* __STL_HAS_WCHAR_T */
- template <class _ForwardIterator,
class _Tp>
- inline void
- __destroy(_ForwardIterator __first, _ForwardIterator __last, _Tp*)
- {
- typedef typename __type_traits<_Tp>::has_trivial_destructor _Trivial_destructor;
- __destroy_aux(__first, __last, _Trivial_destructor());
- }
- template <class _ForwardIterator>
- void
- __destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type)
- {
- for ( ; __first != __last; ++__first)
- destroy(&*__first);
- }
- template <class _ForwardIterator>
- inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {}
- template <class _Tp>
- inline void destroy(_Tp* __pointer) {
- _Destroy(__pointer);
- }
- template <class _ForwardIterator>
- inline void destroy(_ForwardIterator __first, _ForwardIterator __last) {
- _Destroy(__first, __last);
- }

两个问题:
1. C++并不支持通过value_type来判断指针所指对象的类型,STL中通过什么来判断?
2. C++也不支持判断一个对象是否存在析构函数,STL如何来实现判断?
第一个问题参考头文件<stl_iterator.h>
- template <class interator>
- inline typename iterator_traits<interator>::value_type*
- value_type(const interator&)
- {
- return static_cast<typename iterator_triats<interator>::value_type*>(0);
- }
根本目的就是定义了一个value_type类型的空指针,作用是为了函数重载
第二个问题请参看文章:《神奇的__type_traits》
2、空间的分配与释放
SGI使用双 层配置器,如果分配的空间大于128bytes则采用第一级配置器,直接调用malloc(),free();如果分配的空间小于128bytes则采用 复杂一点的memory pool方式。在SGI中一级配置器的实现依靠的是模板类__malloc_alloc_template,二级配置器依靠模板类 __default_alloc_template
SGI STL中的几种配置器

1.
1、__malloc_alloc_template是一个基础配置器,直接调用malloc和free来分配和释放空间,如果空间不够会调用handler处理过后再重新分配。__malloc_alloc_template配置器被作为一级配置器来使用。
-
- template <int inst>
- class __malloc_alloc_template {
- private:
- static void *oom_malloc(size_t);
- 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 (0 == result) result = oom_malloc(n);
- return result;
- }
- static void deallocate(void *p,
size_t )
- {
- free(p);
- }
- static void * reallocate(void *p,
size_t ,
size_t new_sz)
- {
- void * result = realloc(p, new_sz);
- if (0 == result) result = oom_realloc(p, new_sz);
- return result;
- }
- static void (* set_malloc_handler(void (*f)()))()
- {
- void (* old)() = __malloc_alloc_oom_handler;
- __malloc_alloc_oom_handler = f;
- return(old);
- }
- };
- template <int inst>
- void * __malloc_alloc_template<inst>::oom_malloc(size_t n)
- {
- void (* my_malloc_handler)();
- void *result;
- for (;;) {
- my_malloc_handler = __malloc_alloc_oom_handler;
- if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }
- (*my_malloc_handler)();
- result = malloc(n);
- if (result)
return(result);
- }
- }
- template <int inst>
- void * __malloc_alloc_template<inst>::oom_realloc(void *p,
size_t n)
- {
- void (* my_malloc_handler)();
- void *result;
- for (;;) {
- my_malloc_handler = __malloc_alloc_oom_handler;
- if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }
- (*my_malloc_handler)();
- result = realloc(p, n);
- if (result) return(result);
- }
- }
- typedef __malloc_alloc_template<0> malloc_alloc;
2.、__default_alloc_template也是一个基础配置器,和__malloc_alloc_template相比多了一下几个功能:
A. 多线程的支持,包括资源的加锁解锁
B. 分配空间的大小按8字节对齐
C. 如果分配的空间大于128字节,直接调用一级配置器的malloc_alloc::alloc函数来分配
D. 维护了16个自由空间的链表(free list),每个链表都链接了一串相等大小的自由空间,各个链表节点的自由空间大小不同,但都是8的倍数(8、16、24......96、104、112、120、128刚好16级)
E. 如果分配的空间小于128字节,找到一个链表,使得该链表节点的自由空间大小大于请求分配的空间而又与之大小最接近,从该链表中取出一个节点分配
F. 维护了一个memory pool,如果所有自由空间列表为空就会从记忆池中申请空间,记忆池尽量返回可用的空间。
__default_alloc_template 通常被作为二级配置器来使用,由于该类的代码非常繁杂,这里就不列出来了。有兴趣可以阅读STL的源代码。
- template <bool threads,
int inst>
- class __default_alloc_template {
- ...
- };
- typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
- typedef __default_alloc_template<false, 0> single_client_alloc;