事实上SGI STL仍然提供了一个标准的配置器接口,只是把它做了一层隐藏。这个标准接口的配置器名为simple_alloc。 SGI STL的配置器与众不同,也与标准规范不同,其名称是alloc而非allocator,而且不接受任何参数。换句话说,如果你要在程序中明白采用SGI配置器,则不能采用标准写法: vector<int, std::allocator<int> > iv; 必须这么写: vector<int, std::alloc> iv; SGI STL的每个容器都已经指定其缺省的空间配置器为alloc。例如下面的vector声明: template<class T, class Alloc = alloc> class vector{...}; 虽然SGI也定义有一个符合部分标准、名为allocator的配置器,但SGI自己从未用过它,也不建议我们使用,在defalloc.h中。 为了精密分工,STL allocator决定将这两阶段操作区分开来。内存配置操作由alloc::allocate()负责,内存释放操作由alloc::deallocate()负责;对象构造操作由::construct()负责,对象析构操作由::destroy()负责。 STL标准规格告诉我们,配置器定义于<memory>之中,SGI <memory>内含以下两个文件: #include <stl_alloc.h> #include <stl_construct.h> STL还规定配置器必须拥有名为constuct()和destroy()的两个成员函数,然而真正在SGI STL中大显身手的个名为std::alloc的配置器并未遵守这一规则。 下面是<stl_construct.h>的部分内容: #include <new.h> template<class T1, class T2> inline void construct(T1 * p, const T2& value) { new(p) T1(value); } template<class T> inline void destroy(T * pointer) { pointer->~T(); } template<class ForwardIterator> inline void destroy(ForwardIterator first, ForwardIterator last) { __destroy(first, last, value_type(first)); } template<class ForwardIterator, class T> inline void __destroy(ForwardIterator first, ForwardIterator last, T*) { typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor; __destroy_aux(first, last, trivial_destructor()); } template<class ForwardIterator> __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){} inline void destroy(char *, char *){} inline void destroy(wchar_t *, wchar_t *){} ........... stl_iterator相关文件里有以下内容: template<class Iterator> inline typename iterator_traits<Iterator>::value_type* value_type(const Iterator&) { return static_cast<typename iterator_traits<Iterator>::value_type *>(0); } template<class Iterator> struct iterator_traits { typedef typename Iterator::iterator_category iterator_category; typedef typename Iterator::value_type value_type; typedef typename Iterator::defference_type difference_type; typedef typename Iterator::pointer pointer; typedef typename Iterator::reference reference; }; template<class T> struct iterator_traits<T*> { typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef T& reference; }; template<class T> struct iterator_traits<const T*> { typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef const T* pointer; typedef const T& reference; }; struct __true_type{}; struct __false_type{}; template<class type> struct __type_traits { typedef __true_type this_dummy_member_must_be_first; typedef __false_type has_trivial_default_constructor; typedef __false_type has_trivial_copy_constructor; typedef __false_type has_trivial_assignment_operator; typedef __false_type has_trivial_destructor; typedef __false_type is_POD_type; }; 然后接着是各种基本数据类型的特化版本。 整个设计究竟只开放第一级配置器,或是同时开放第二级配置器,取决于__USE_MALLOC是否被定义。 以下模板参数0没有派上用场 typedef __malloc_alloc_template<0> malloc_alloc #ifdef __USE_MALLOC typedef malloc_alloc alloc #else typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc #endif /* !__USE_MALLOC*/ 第一级配置器: #if 0 # include <new> # define __THROW_BAD_ALLOC throw bad_alloc #elif !define(__THROW_BAD_ALLOC) # include <iostream> # define __THROW_BAD_ALLOC cerr << "out of memory" << endl; exit(1) #endif 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 /*old_sz*/, 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>::__malloc_alloc_oom_handler)() = 0; 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); } } 请注意,SGI第一级配置器allocate()和reallocate()都是在调用malloc()和realloc()不成功后,改调用oom_malloc()和oom_realloc()。后两者都有内循环,不断调用“内存不足处理例程”,期望在某次调用之后,获得足够的内存而圆满完成任务。但如果“内存不足处理例程”并未被客端设定,oom_malloc()和oom_realloc()便老实不客气地调用__THROW_BAD_ALLOC,丢出bad_alloc异常信息,或利用exit(1)硬生生中止程序。 记住,设计“内存不足处理例程”是客端的责任,设定“内存不足处理例程”也是客端的责任。再一次提醒你,“内存不足处理例程”解决问题的做法有着特定的模式。 enum {__ALIGN = 8}; enum {__MAX_BYTES = 128}; enum {__NFREELISTS = __MAX_BYTES / __ALIGN}; template<bool threads, int inst> class __default_alloc_template { private: static size_t ROUND_UP(size_t bytes) { return (((bytes) + __ALIGN - 1) & ~(__ALIGN - 1)); } private: union obj { union obj* free_list_link; char client_data[1]; }; private: static obj * volatile free_list[__NFREELISTS]; static size_t FREELIST_INDEX(size_t bytes) { return (((bytes) + __ALIGN - 1) / __ALIGN - 1); } //返回一个大小为n的对象,并可能加入大小为n的其它区块到free_list static void * refill(size_t n); static char * chunk_alloc(size_t size, int& nobjs); static char * start_free; static char * end_free; static size_t heap_size; public: static void * allocate(size_t n); static void deallocate(void * p, size_t n); static void * reallocate(void * p, size_t old_sz, size_t new_sz); }; template<bool threads, int inst> char * __default_alloc_template<threads, inst>::start_free = 0; template<bool threads, int inst> char * __default_alloc_template<threads, inst>::end_free = 0; template<bool threads, int inst> size_t __default_alloc_template<threads, inst>::heap_size = 0; template<bool threads, int inst> __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> void * __default_alloc_template<threads, inst>::allocate(size_t n) { obj * volatile * my_free_list; obj * result; if(n > (size_t)__MAX_BYTES) { return(malloc_alloc::allocate(n)); } my_free_list = free_list + FREELIST_INDEX(n); result = *my_free_list; if(0 == result) { void * r = refill(ROUND_UP(n)); return r; } *my_free_list = result->free_list_link; return result; } template<bool threads, int inst> void __default_alloc_template<threads, inst>::deallocate(void * p, size_t n) { obj * q = (obj *)p; obj * volatile * my_free_list; if(n > (size_t)__MAX_BYTES) { malloc_alloc::deallocate(p, n); return; } my_free_list = free_list + FREELIST_INDEX(n); q->free_list_link = *my_free_list; *my_free_list = q; } //假设n已经适当上调至8的倍数 template<bool threads, int inst> void * __default_alloc_template<threads, inst>::refill(size_t n) { int nobjs = 20; char * chunk = chunk_alloc(n, nobjs); obj * volatile * my_free_list; obj * result; obj * current_obj, * next_obj; int i; if(1 == nobjs) return chunk; my_free_list = free_list + FREELIST_INDEX(n); *my_free_list = next_obj = (obj *)(chunk + n); result = (obj *)chunk; for(i = 1; ; i++) { current_obj = next_obj; next_obj = (obj *)((char *)next_obj + n); if(nobjs - 1 == i) { current_obj->free_list_link = 0; break; } else { current_obj->free_list_link = next_obj; } } return (result); } template<bool threads, int inst> char * __default_alloc_template<threads, inst>::chunk_alloc(size_t size, int & nobjs) { char * result; size_t total_bytes = size * nobjs; size_t bytes_left = end_free - start_free; if(bytes_left >= total_bytes) { result = start_free; start_free += total_bytes; return result; } else if(bytes_left >= size) { nobjs = bytes_left / size; total_bytes = size * nobjs; result = start_free; start_free += total_bytes; return result; } else { size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); if(bytes_left > 0) { obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left); ((obj *)start_free)->free_list_link = *my_free_list; *my_free_list = (obj *)start_free; } start_free = (char *)malloc(bytes_to_get); if(0 == start_free) { int i; obj * volatile * my_free_list, *p; for(i = size; i <= __MAX_BYTES; i += __ALIGN) { my_free_list = free_list + FREELIST_INDEX(i); p = *my_free_list; if(0 != p) { *my_free_list = p->free_list_link; start_free = (char *)p; end_free = start_free + i; return (chunk_alloc(size, nobjs)); } } end_free = 0; start_free = (char *)malloc_alloc::allocate(bytes_to_get); } end_free = start_free + bytes_to_get; heap_size += bytes_to_get; return chunk_alloc(size, nobjs); } } template<class InputIterator, class ForwardIterator> ForwardIterator uninitialized_copy(InputIterator first, InputIterator last, ForwardIterator result); template<class ForwardIterator, class T> void uninitialized_fill(ForwardIterator first, ForwardIterator last, const T& x); template<class ForwardIterator, class Size, class T> ForwardIterator uninitialized_fill_n(ForwardIterator first, Size n, const T& x); POD型别必然拥有trivial ctor/dtor/copy/assignment函数。
stl2
最新推荐文章于 2024-10-06 17:22:55 发布