在第一级配置器中,一开始就定义了内存分配出错的宏接口,如下:
#ifndef __THROW_BAD_ALLOC # if defined(__STL_NO_BAD_ALLOC) || !defined(__STL_USE_EXCEPTIONS) # include <stdio.h> # include <stdlib.h> # define __THROW_BAD_ALLOC fprintf(stderr, "out of memory "); exit(1) # else /* Standard conforming out-of-memory handling */ # include <new> # define __THROW_BAD_ALLOC throw std::bad_alloc() # endif #endif
先弄清楚第一级配置器如何工作,注意没有template型别参数,因为我们只是分配空间,并不进行对象的构造,至于非型别参数 "__inst" ,就没怎么派上用场,如下:
template <int __inst> class __malloc_alloc_template { private: //以下函数是处理内存不足的情况 static void* _S_oom_malloc(size_t); static void* _S_oom_realloc(void*, size_t); #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG //无法处理模板类中的静态成员 static void (* __malloc_alloc_oom_handler)(); #endif public: static void* allocate(size_t __n) { void* __result = malloc(__n); if (0 == __result) __result = _S_oom_malloc(__n); //分配的内存无法满足需求时,调用_S_oom_malloc()函数 return __result; } //内存释放函数 static void deallocate(void* __p, size_t /* __n */) { free(__p); } //内存重配置函数 static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz) { void* __result = realloc(__p, __new_sz); if (0 == __result) __result = _S_oom_realloc(__p, __new_sz); return __result; } //你可以通过该函数指定自己的 out-of-memory handler static void (* __set_malloc_handler(void (*__f)()))() { void (* __old)() = __malloc_alloc_oom_handler; __malloc_alloc_oom_handler = __f; return(__old); }
}; // malloc_alloc out-of-memory handling #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG template <int __inst> void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0; #endif template <int __inst> void* __malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n) { void (* __my_malloc_handler)(); void* __result; for (;;) { //和_S_oom_realloc类似 __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>::_S_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); } }
第一级配置器以 malloc()、free()、realloc()等 C 函数执行实际的内存配置、释放、重配置操作,并实现出类似 C++ new-handler 的机制,不过并不能直接使用 C++ new-handler 机制, 因为其并非使用 ::operator new 来配置内存。
C++ new-handler 机制是,可以要求系统在配置需求无法被满足时,调用你所指定的函数。一旦 ::operator new 无法完成任务,在丢出 std::bad_alloc 异常状态之前,会先调用由客端指定的处理例程,该处理例程通常被称为new-handler。
《effective C++ handler》 中介绍了 new-handler 的内存不足处理模式,详情见下篇。
SGI 以malloc 而非 ::operator new 来配置内存,原因是C++并未提供相应于 realloc() 的内存配置操作,当然不排除还有些历史因素。因此,SGI 不能使用 C++ 的 set_new_handler() ,必需仿真一个类似类似的 set_new_handler().
SGI 第一级配置器 allocate() 和 realloc() 都是在调用 malloc() 和 realloc() 不成功后,改调用 oom_malloc() 和 oom_realloc()。后两者都有死循环,不断调用 "内存不足处理例程" ,期望在某次调用的时候,能够得到充足的内存分配而完成任务。但如果“内存不足处理例程”并没有被客端设定, oom_malloc() 和 oom_realloc() 便会直接调用 __THROW_BAD_ALLOC,丢出 bad_alloc 异常信息,或利用 exit(1)终止程序。