#ifndef __SGI_STL_INTERNAL_ALLOC_H #define __SGI_STL_INTERNAL_ALLOC_H #ifdef __SUNPRO_CC # define __PRIVATE public // Extra access restrictions prevent us from really making some things // private. #else # define __PRIVATE private #endif #ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG # define __USE_MALLOC #endif // This implements some standard node allocators. These are // NOT the same as the allocators in the C++ draft standard or in // in the original STL. They do not encapsulate different pointer // types; indeed we assume that there is only one pointer type. // The allocation primitives are intended to allocate individual objects, // not larger arenas as with the original STL allocators. #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\n"); exit(1) # else /* Standard conforming out-of-memory handling */ # include <new> # define __THROW_BAD_ALLOC throw std::bad_alloc() # endif #endif #include <stddef.h> #include <stdlib.h> #include <string.h> #include <assert.h> #ifndef __RESTRICT # define __RESTRICT #endif #ifdef __STL_THREADS # include <stl_threads.h> # define __NODE_ALLOCATOR_THREADS true # ifdef __STL_SGI_THREADS // We test whether threads are in use before locking. // Perhaps this should be moved into stl_threads.h, but that // probably makes it harder to avoid the procedure call when // it isn't needed. extern "C" { extern int __us_rsthread_malloc; } // The above is copied from malloc.h. Including <malloc.h> // would be cleaner but fails with certain levels of standard // conformance. # define __NODE_ALLOCATOR_LOCK if (threads && __us_rsthread_malloc) \ { _S_node_allocator_lock._M_acquire_lock(); } # define __NODE_ALLOCATOR_UNLOCK if (threads && __us_rsthread_malloc) \ { _S_node_allocator_lock._M_release_lock(); } # else /* !__STL_SGI_THREADS */ # define __NODE_ALLOCATOR_LOCK \ { if (threads) _S_node_allocator_lock._M_acquire_lock(); } # define __NODE_ALLOCATOR_UNLOCK \ { if (threads) _S_node_allocator_lock._M_release_lock(); } # endif #else // Thread-unsafe # define __NODE_ALLOCATOR_LOCK # define __NODE_ALLOCATOR_UNLOCK # define __NODE_ALLOCATOR_THREADS false #endif __STL_BEGIN_NAMESPACE #if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) #pragma set woff 1174 #endif // Malloc-based allocator. Typically slower than default alloc below. // Typically thread-safe and more storage efficient. #ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG # ifdef __DECLARE_GLOBALS_HERE void (* __malloc_alloc_oom_handler)() = 0; // g++ 2.7.2 does not handle static template data members. # else extern void (* __malloc_alloc_oom_handler)(); # endif #endif //第一级配置
template <int __inst> class __malloc_alloc_template { private:
//oom:out of memory三个处理函数,因为是以malloc(),free(),realloc()等C函数执行实际内存分配释放,所以不能直接使用C++ new-handler机制(set_new_handler()),因为不是用::operator new来分配。
//如果未设定处理函数,则抛出std::bad_alloc
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); 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; } 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 (;;) { __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); } } typedef __malloc_alloc_template<0> malloc_alloc; // allocate->_S_oom_malloc->__malloc_alloc_oom_handler // reallocate->_S_oom_realloc->__malloc_alloc_oom_handler //-------------------------------------------------------------------------------------------------- //外面再多一层封装,使Alloc具备标准接口
template<class _Tp, class _Alloc> class simple_alloc { public: static _Tp* allocate(size_t __n) { return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp)); } static _Tp* allocate(void) { return (_Tp*) _Alloc::allocate(sizeof (_Tp)); } static void deallocate(_Tp* __p, size_t __n) { if (0 != __n) _Alloc::deallocate(__p, __n * sizeof (_Tp)); } static void deallocate(_Tp* __p) { _Alloc::deallocate(__p, sizeof (_Tp)); } }; // Allocator adaptor to check size arguments for debugging. // Reports errors using assert. Checking can be disabled with // NDEBUG, but it's far better to just use the underlying allocator // instead when no checking is desired. // There is some evidence that this can confuse Purify.
//带有debug功能的allocator适配器
//新分配地址的头部额外多增加8个字节,用来存放新分配空间的大小,dealloate和reallocate会验证传入的释放空间大小是否等于待释放地址头部8个字节存放的空间大小,
//如果不相等,则abort。
template <class _Alloc> class debug_alloc { private: enum {_S_extra = 8}; //存储分配空间大小所需的位数
public: static void* allocate(size_t __n) { char* __result = (char*)_Alloc::allocate(__n + (int) _S_extra); *(size_t*)__result = __n; return __result + (int) _S_extra; } static void deallocate(void* __p, size_t __n) { char* __real_p = (char*)__p - (int) _S_extra; assert(*(size_t*)__real_p == __n); _Alloc::deallocate(__real_p, __n + (int) _S_extra); } static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz) { char* __real_p = (char*)__p - (int) _S_extra; assert(*(size_t*)__real_p == __old_sz); char* __result = (char*) _Alloc::reallocate(__real_p, __old_sz + (int) _S_extra, __new_sz + (int) _S_extra); *(size_t*)__result = __new_sz; return __result + (int) _S_extra; } }; # ifdef __USE_MALLOC typedef malloc_alloc alloc; typedef malloc_alloc single_client_alloc; # else // Sun C++ compiler需要在类外定义这些枚举 #if defined(__SUNPRO_CC) || defined(__GNUC__) // breaks if we make these template class members: enum {_ALIGN = 8}; //小型区块的上调边界 enum {_MAX_BYTES = 128}; //小型区块的上限 enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN free-lists 个数 #endif//第二级配置器 // 默认的node allocator // 如果有合适的编译器, 速度上与原始的STL class-specific allocators大致等价 // 但是具有产生更少内存碎片的优点 // Default_alloc_template参数是用于实验性质的, 在未来可能会消失 // 客户只能在当下使用alloc // // 重要的实现属性: // 1. 如果客户请求一个size > __MAX_BYTE的对象, 则直接使用malloc()分配 // 2. 对于其它情况下, 我们将请求对象的大小按照内存对齐向上舍入ROUND_UP(requested_size) // Thus the client has enough size information that we can return the object to the proper free list // without permanently losing part of the object. // // 第一个模板参数指定是否有多于一个线程使用本allocator // 在一个default_alloc实例中分配对象, 在另一个deallocate实例中释放对象, 是安全的 // 这有效的转换其所有权到另一个对象 // 这可能导致对我们引用的区域产生不良影响 // 第二个模板参数仅仅用于创建多个default_alloc实例 // 不同容器使用不同allocator实例创建的node拥有不同类型, 这限制了此方法的通用性 //allocate()大于128调用第一级适配器,否则在free-list中找,没找到调用refill()从内存池中填充到free-list //refill()调用chunk_alloc从内存池中取空间给free-list使用,然后调整free-list链表
//内存池中有20个或有但不满20个,则调整内存池的起始位置,返回内存地址,
//内存池中一个也不能满足了,则如果内存池中还要剩余的一些零头,则编入free-list,然后尝试从heap分配40+n块空间到内存池。分配成功后递归调用返回空间地址 //heap上也没有空间分配了,则先检查free-list中有没比所需块大一点的空间,有则分配给他,堆上也没有则调用第一级配置器,改善或排除异常。
template <bool threads, int inst> class __default_alloc_template { private: // Really we should use static const int x = N // instead of enum { x = N }, but few compilers accept the former. #if ! (defined(__SUNPRO_CC) || defined(__GNUC__)) enum {_ALIGN = 8}; enum {_MAX_BYTES = 128}; enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN # endif static size_t _S_round_up(size_t __bytes) //向上调整至的倍数 { return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); } __PRIVATE: union _Obj { union _Obj* _M_free_list_link; char _M_client_data[1]; /* The client sees this. */ //柔性数组 };
/* int main() { _Obj* test = (_Obj*)"12345678"; cout << sizeof(_Obj) <<endl; //4 cout<<test->_M_client_data<<endl; //"12345678" return 0; } */
private: # if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC) static _Obj* __STL_VOLATILE _S_free_list[]; ////volatile关键字,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。 // Specifying a size results in duplicate def for 4.1 # else static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS]; # endif static size_t _S_freelist_index(size_t __bytes) { //根据区块大小,使用第n号free-list return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);//向上取,再减1 } // // 返回一个大小为n的对象,并可能加入大小为n的其他区块到free-list static void* _S_refill(size_t __n); //配置一大块空间,可容纳nobjs个大小为size的区块
//如果配置nobjs个区块有所不便,nobjs可能会减小
static char* _S_chunk_alloc(size_t __size, int& __nobjs); // Chunk allocation state. static char* _S_start_free; // 内存池起始点 static char* _S_end_free; // 内存池结束点 static size_t _S_heap_size; // 已经在堆上分配的空间大小 # ifdef __STL_THREADS static _STL_mutex_lock _S_node_allocator_lock; # endif // It would be nice to use _STL_auto_lock here. But we // don't need the NULL check. And we do need a test whether // threads have actually been started. class _Lock; friend class _Lock; class _Lock { public: _Lock() { __NODE_ALLOCATOR_LOCK; } ~_Lock() { __NODE_ALLOCATOR_UNLOCK; } }; public: /* __n must be > 0 */ static void* allocate(size_t __n) { void* __ret = 0; // 如果待分配对象大于__MAX_BYTES, 使用一级配置器分配 if (__n > (size_t) _MAX_BYTES) { __ret = malloc_alloc::allocate(__n); } else { _Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__n); // Acquire the lock here with a constructor call. // This ensures that it is released in exit or during stack // unwinding. # ifndef _NOTHREADS /*REFERENCED*/ _Lock __lock_instance; # endif _Obj* __RESTRICT __result = *__my_free_list; //没有可用区块,就填充空间
if (__result == 0) __ret = _S_refill(_S_round_up(__n)); else { *__my_free_list = __result -> _M_free_list_link; __ret = __result; } } return __ret; }; /* __p may not be 0 */ static void deallocate(void* __p, size_t __n) { if (__n > (size_t) _MAX_BYTES) malloc_alloc::deallocate(__p, __n); else { _Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__n); _Obj* __q = (_Obj*)__p; // acquire lock # ifndef _NOTHREADS /*REFERENCED*/ _Lock __lock_instance; # endif /* _NOTHREADS */ __q -> _M_free_list_link = *__my_free_list; *__my_free_list = __q; // lock is released here } } static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz); } ; typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc; typedef __default_alloc_template<false, 0> single_client_alloc;//任意两个allocator都是可互换的;如果a1和a2的类型都是allocator,我们可以自由地通过a1来allocate()内存然后通过 a2来deallocate()它。
//我们因此定义一个比较操作以表明所有allocator对象是等价的
template <bool __threads, int __inst> inline bool operator==(const __default_alloc_template<__threads, __inst>&, const __default_alloc_template<__threads, __inst>&) { return true; } # ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER template <bool __threads, int __inst> inline bool operator!=(const __default_alloc_template<__threads, __inst>&, const __default_alloc_template<__threads, __inst>&) { return false; } # endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */ // 每次分配一大块内存, 防止多次分配小内存块带来的内存碎片 // 进行分配操作时, 根据具体环境决定是否加锁 // 我们假定要分配的内存满足内存对齐要求 template <bool __threads, int __inst> char* __default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size, int& __nobjs) { char* __result; size_t __total_bytes = __size * __nobjs; size_t __bytes_left = _S_end_free - _S_start_free; // 计算内存池剩余容量 // 如果内存池中剩余内存>=需要分配的内内存, 返回start_free指向的内存块, // 并且重新设置内存池起始点 if (__bytes_left >= __total_bytes) { __result = _S_start_free; _S_start_free += __total_bytes; return(__result); } // 如果内存吃中剩余的容量不够分配, 但是能至少分配一个节点时, // 返回所能分配的最多的节点, 返回start_free指向的内存块 // 并且重新设置内存池起始点 else if (__bytes_left >= __size) { __nobjs = (int)(__bytes_left/__size); __total_bytes = __size * __nobjs; __result = _S_start_free; _S_start_free += __total_bytes; return(__result); } // 内存池剩余内存连一个节点也不够分配 else { size_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4); // Try to make use of the left-over piece. // 将剩余的内存分配给指定的free_list[FREELIST_INDEX(bytes_left)] if (__bytes_left > 0) { _Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__bytes_left); ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list; *__my_free_list = (_Obj*)_S_start_free; } //配置heap空间,补充内存池 _S_start_free = (char*)malloc(__bytes_to_get); if (0 == _S_start_free) { //malloc()失败 size_t __i; _Obj* __STL_VOLATILE* __my_free_list; _Obj* __p; //尝试检查我们手上拥有的东西,这不会造成伤害。我们不打算尝试配置较小的区块。 //因为那在多进程机器上容易造成灾难 //以下搜寻未使用的比size大的free-list区块 for (__i = __size; __i <= (size_t) _MAX_BYTES; __i += (size_t) _ALIGN) { __my_free_list = _S_free_list + _S_freelist_index(__i); __p = *__my_free_list; if (0 != __p) { //有未用区块则调整free-list以释出未用区块 *__my_free_list = __p -> _M_free_list_link; _S_start_free = (char*)__p; _S_end_free = _S_start_free + __i; //递归调用自己,以修正nobjs return(_S_chunk_alloc(__size, __nobjs)); //注意,残余零头终将被编入适当的free-list中备用 } } _S_end_free = 0; // In case of exception. _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get); //调用第一级配置器,看看out-of-memory机制能否尽力,或抛出异常。 } _S_heap_size += __bytes_to_get; _S_end_free = _S_start_free + __bytes_to_get; return(_S_chunk_alloc(__size, __nobjs)); } } // 返回一个大小为n的对象, 并且加入到free_list[FREELIST_INDEX(n)] // 进行分配操作时, 根据具体环境决定是否加锁 // 我们假定要分配的内存满足内存对齐要求 template <bool __threads, int __inst> void* __default_alloc_template<__threads, __inst>::_S_refill(size_t __n) { int __nobjs = 20; char* __chunk = _S_chunk_alloc(__n, __nobjs); _Obj* __STL_VOLATILE* __my_free_list; _Obj* __result; _Obj* __current_obj; _Obj* __next_obj; int __i; if (1 == __nobjs) return(__chunk); __my_free_list = _S_free_list + _S_freelist_index(__n); /* Build free list in chunk */ __result = (_Obj*)__chunk; *__my_free_list = __next_obj = (_Obj*)(__chunk + __n); for (__i = 1; ; __i++) { __current_obj = __next_obj; __next_obj = (_Obj*)((char*)__next_obj + __n); if (__nobjs - 1 == __i) { __current_obj -> _M_free_list_link = 0; break; } else { __current_obj -> _M_free_list_link = __next_obj; } } return(__result); } template <bool threads, int inst> void* __default_alloc_template<threads, inst>::reallocate(void* __p, size_t __old_sz, size_t __new_sz) { void* __result; size_t __copy_sz; if (__old_sz > (size_t) _MAX_BYTES && __new_sz > (size_t) _MAX_BYTES) { return(realloc(__p, __new_sz)); } if (_S_round_up(__old_sz) == _S_round_up(__new_sz)) return(__p); __result = allocate(__new_sz); __copy_sz = __new_sz > __old_sz? __old_sz : __new_sz; memcpy(__result, __p, __copy_sz); deallocate(__p, __old_sz); return(__result); } #ifdef __STL_THREADS template <bool __threads, int __inst> _STL_mutex_lock __default_alloc_template<__threads, __inst>::_S_node_allocator_lock __STL_MUTEX_INITIALIZER; #endif template <bool __threads, int __inst> char* __default_alloc_template<__threads, __inst>::_S_start_free = 0; template <bool __threads, int __inst> char* __default_alloc_template<__threads, __inst>::_S_end_free = 0; template <bool __threads, int __inst> size_t __default_alloc_template<__threads, __inst>::_S_heap_size = 0; template <bool __threads, int __inst> typename __default_alloc_template<__threads, __inst>::_Obj* __STL_VOLATILE __default_alloc_template<__threads, __inst> ::_S_free_list[ # if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC) _NFREELISTS # else __default_alloc_template<__threads, __inst>::_NFREELISTS # endif ] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; // The 16 zeros are necessary to make version 4.1 of the SunPro // compiler happy. Otherwise it appears to allocate too little // space for the array. #endif /* ! __USE_MALLOC */ // This implements allocators as specified in the C++ standard. // // Note that standard-conforming allocators use many language features // that are not yet widely implemented. In particular, they rely on // member templates, partial specialization, partial ordering of function // templates, the typename keyword, and the use of the template keyword // to refer to a template member of a dependent type. #ifdef __STL_USE_STD_ALLOCATORS template <class _Tp> class allocator { typedef alloc _Alloc; // The underlying allocator. public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef _Tp* pointer; typedef const _Tp* const_pointer; typedef _Tp& reference; typedef const _Tp& const_reference; typedef _Tp value_type; template <class _Tp1> struct rebind { typedef allocator<_Tp1> other; }; allocator() __STL_NOTHROW {} allocator(const allocator&) __STL_NOTHROW {} template <class _Tp1> allocator(const allocator<_Tp1>&) __STL_NOTHROW {} ~allocator() __STL_NOTHROW {} pointer address(reference __x) const { return &__x; } const_pointer address(const_reference __x) const { return &__x; } // __n is permitted to be 0. The C++ standard says nothing about what // the return value is when __n == 0. _Tp* allocate(size_type __n, const void* = 0) { return __n != 0 ? static_cast<_Tp*>(_Alloc::allocate(__n * sizeof(_Tp))) : 0; } // __p is not permitted to be a null pointer. void deallocate(pointer __p, size_type __n) { _Alloc::deallocate(__p, __n * sizeof(_Tp)); } size_type max_size() const __STL_NOTHROW { return size_t(-1) / sizeof(_Tp); } void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); } void destroy(pointer __p) { __p->~_Tp(); } }; template<> class allocator<void> { public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef void* pointer; typedef const void* const_pointer; typedef void value_type; template <class _Tp1> struct rebind { typedef allocator<_Tp1> other; }; }; template <class _T1, class _T2> inline bool operator==(const allocator<_T1>&, const allocator<_T2>&) { return true; } template <class _T1, class _T2> inline bool operator!=(const allocator<_T1>&, const allocator<_T2>&) { return false; } // Allocator adaptor to turn an SGI-style allocator (e.g. alloc, malloc_alloc) // into a standard-conforming allocator. Note that this adaptor does // *not* assume that all objects of the underlying alloc class are // identical, nor does it assume that all of the underlying alloc's // member functions are static member functions. Note, also, that // __allocator<_Tp, alloc> is essentially the same thing as allocator<_Tp>. template <class _Tp, class _Alloc> struct __allocator { _Alloc __underlying_alloc; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef _Tp* pointer; typedef const _Tp* const_pointer; typedef _Tp& reference; typedef const _Tp& const_reference; typedef _Tp value_type; template <class _Tp1> struct rebind { typedef __allocator<_Tp1, _Alloc> other; }; __allocator() __STL_NOTHROW {} __allocator(const __allocator& __a) __STL_NOTHROW : __underlying_alloc(__a.__underlying_alloc) {} template <class _Tp1> __allocator(const __allocator<_Tp1, _Alloc>& __a) __STL_NOTHROW : __underlying_alloc(__a.__underlying_alloc) {} ~__allocator() __STL_NOTHROW {} pointer address(reference __x) const { return &__x; } const_pointer address(const_reference __x) const { return &__x; } // __n is permitted to be 0. _Tp* allocate(size_type __n, const void* = 0) { return __n != 0 ? static_cast<_Tp*>(__underlying_alloc.allocate(__n * sizeof(_Tp))) : 0; } // __p is not permitted to be a null pointer. void deallocate(pointer __p, size_type __n) { __underlying_alloc.deallocate(__p, __n * sizeof(_Tp)); } size_type max_size() const __STL_NOTHROW { return size_t(-1) / sizeof(_Tp); } void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); } void destroy(pointer __p) { __p->~_Tp(); } }; template <class _Alloc> class __allocator<void, _Alloc> { typedef size_t size_type; typedef ptrdiff_t difference_type; typedef void* pointer; typedef const void* const_pointer; typedef void value_type; template <class _Tp1> struct rebind { typedef __allocator<_Tp1, _Alloc> other; }; }; template <class _Tp, class _Alloc> inline bool operator==(const __allocator<_Tp, _Alloc>& __a1, const __allocator<_Tp, _Alloc>& __a2) { return __a1.__underlying_alloc == __a2.__underlying_alloc; } #ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER template <class _Tp, class _Alloc> inline bool operator!=(const __allocator<_Tp, _Alloc>& __a1, const __allocator<_Tp, _Alloc>& __a2) { return __a1.__underlying_alloc != __a2.__underlying_alloc; } #endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */ // Comparison operators for all of the predifined SGI-style allocators. // This ensures that __allocator<malloc_alloc> (for example) will // work correctly. template <int inst> inline bool operator==(const __malloc_alloc_template<inst>&, const __malloc_alloc_template<inst>&) { return true; } #ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER template <int __inst> inline bool operator!=(const __malloc_alloc_template<__inst>&, const __malloc_alloc_template<__inst>&) { return false; } #endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */ template <class _Alloc> inline bool operator==(const debug_alloc<_Alloc>&, const debug_alloc<_Alloc>&) { return true; } #ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER template <class _Alloc> inline bool operator!=(const debug_alloc<_Alloc>&, const debug_alloc<_Alloc>&) { return false; } #endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */ // Another allocator adaptor: _Alloc_traits. This serves two // purposes. First, make it possible to write containers that can use // either SGI-style allocators or standard-conforming allocator. // Second, provide a mechanism so that containers can query whether or // not the allocator has distinct instances. If not, the container // can avoid wasting a word of memory to store an empty object. // This adaptor uses partial specialization. The general case of // _Alloc_traits<_Tp, _Alloc> assumes that _Alloc is a // standard-conforming allocator, possibly with non-equal instances // and non-static members. (It still behaves correctly even if _Alloc // has static member and if all instances are equal. Refinements // affect performance, not correctness.) // There are always two members: allocator_type, which is a standard- // conforming allocator type for allocating objects of type _Tp, and // _S_instanceless, a static const member of type bool. If // _S_instanceless is true, this means that there is no difference // between any two instances of type allocator_type. Furthermore, if // _S_instanceless is true, then _Alloc_traits has one additional // member: _Alloc_type. This type encapsulates allocation and // deallocation of objects of type _Tp through a static interface; it // has two member functions, whose signatures are // static _Tp* allocate(size_t) // static void deallocate(_Tp*, size_t) // The fully general version. /* 另一个allocator适配器_Alloc_traits, 它有两个意图 1.使容易既看使用sgi风格的allocator,也可以使用标准规格的allocator 2.提供了一种机制:容器可以查询allocator是否拥有明确的实体,如果没有,容易可以避免浪费内存去存储空对象 这个适配器使用了偏特化。 _Alloc_traits<_Tp, _Alloc>假定_Alloc是一个服从标准的allocator,可能有不相等的实例和非静态成员。 (如果各实例相等或拥有静态成员,也表现正确。 优化影响执行效率,不影响正确性) 有两个成员 1. allocator_type 服从标准的分配_Tp类型对象的allocator类型 2. _S_instanceless一个static const bool成员 如果为true,则表示任何两个allocator_type实例之间没有差别,同时,_Alloc_traits有额外的成员_Alloc_type _Alloc_type通过一个静态接口封装了分配和回收_Tp类型对象,它有两个成员函数,原型为 static _Tp* allocate(size_t) static void deallocate(_Tp*, size_t) */ //写自定义分配器的时候也是,必须重写rebind,且不能有非静态成员。并假定任何同类型的allocator相等,即_S_instanceless为true template <class _Tp, class _Allocator> struct _Alloc_traits { static const bool _S_instanceless = false; typedef typename _Allocator::__STL_TEMPLATE rebind<_Tp>::other allocator_type; }; template <class _Tp, class _Allocator> const bool _Alloc_traits<_Tp, _Allocator>::_S_instanceless; // The version for the default allocator. //给默认allocator的版本 template <class _Tp, class _Tp1> struct _Alloc_traits<_Tp, allocator<_Tp1> > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, alloc> _Alloc_type; typedef allocator<_Tp> allocator_type; }; // Versions for the predefined SGI-style allocators. template <class _Tp, int __inst> struct _Alloc_traits<_Tp, __malloc_alloc_template<__inst> > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type; typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type; }; template <class _Tp, bool __threads, int __inst> struct _Alloc_traits<_Tp, __default_alloc_template<__threads, __inst> > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, __default_alloc_template<__threads, __inst> > _Alloc_type; typedef __allocator<_Tp, __default_alloc_template<__threads, __inst> > allocator_type; }; template <class _Tp, class _Alloc> struct _Alloc_traits<_Tp, debug_alloc<_Alloc> > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type; typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type; }; // Versions for the __allocator adaptor used with the predefined // SGI-style allocators. template <class _Tp, class _Tp1, int __inst> struct _Alloc_traits<_Tp, __allocator<_Tp1, __malloc_alloc_template<__inst> > > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, __malloc_alloc_template<__inst> > _Alloc_type; typedef __allocator<_Tp, __malloc_alloc_template<__inst> > allocator_type; }; template <class _Tp, class _Tp1, bool __thr, int __inst> struct _Alloc_traits<_Tp, __allocator<_Tp1, __default_alloc_template<__thr, __inst> > > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, __default_alloc_template<__thr,__inst> > _Alloc_type; typedef __allocator<_Tp, __default_alloc_template<__thr,__inst> > allocator_type; }; template <class _Tp, class _Tp1, class _Alloc> struct _Alloc_traits<_Tp, __allocator<_Tp1, debug_alloc<_Alloc> > > { static const bool _S_instanceless = true; typedef simple_alloc<_Tp, debug_alloc<_Alloc> > _Alloc_type; typedef __allocator<_Tp, debug_alloc<_Alloc> > allocator_type; }; #endif /* __STL_USE_STD_ALLOCATORS */ #if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32) #pragma reset woff 1174 #endif __STL_END_NAMESPACE #undef __PRIVATE #endif /* __SGI_STL_INTERNAL_ALLOC_H */ // Local Variables: // mode:C++ // End: