配置器(allocator)
配置器:负责空间配置与管理,从实现的角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的模板类。
空间配置器意义:整个 STL 的操作对象(包含的所有数值)都存放在容器之内,空间配置器就是用来帮助这些容器管理(动态开辟,回收)内存空间,使其可以动态存放内容。
SGI STL 空间配置器的文件结构
SGI STL 的配置器,其名称是 alloc 而不是 allocator,而且不接受任何参数。SGI 也定义有allocator配置器,但是因为效率不佳,SGI 并未使用,也不建议我们使用,它只是基层内存配置/释放行为(::operator::new 和 ::operator::delete)的一层薄薄的包装。被定义在 defalloc.h文件中。
SGI STL 的每一个容器都已经指定其缺省(默认)的空间配置器为 alloc,例如:vector 。
template <class T, class Alloc = alloc> // 默认使用 alloc 为配置器
class vector {...};
vector<int, std::alloc> iv;
空间配置器相关头文件:
- <stl_construct.h>:定义了全局函数 construct() 和 destroy(),负责对象的
构造和析构
。 - <stl_alloc.h>:定义了一、二级配置器,配置器名为 alloc, 处理对象
构造前的空间配置
和 对象析构后的空间释放
。 - <stl_uninitialized.h>:定义了
全局函数
,用来填充(fill)或复制(copy)大块内存数据。 - <defalloc.h>:SGI 标准的空间配置器,std::allocator
- <README.md> :是一些描述信息;(本篇文章中包含这些信息)
空间配置器的文件分析:
defalloc.h 只是为了兼容,SGI并不使用,那么我们如何处理配置器的new和delete操作呢?
已知一个对象的创建和销毁如下:
class TestA
{...}
TestA* test = new TestA; //1.分配对象内存空间 2.调用构造函数初始化对象
delete test; //1.调用对象析构函数 2.释放对象占用内存空间
结论:
new 的操作中需要完成两件事:(1,2就是先后顺序)
1.分配对象内存空间
(stl_alloc.h文件中)alloc::allocate()处理2.调用构造函数初始化对象
(stl_construct.h文件中) construct()处理
delete 的操作中需要完成两件事:(1,2就是先后顺序)
1.调用对象析构函数
(stl_alloc.h文件中)alloc::deallocate()处理2.释放对象占用的内存空间
(stl_construct.h文件中) destroy()处理
所以new 和 delete 的操作全部在 “stl_alloc.h” 和 “stl_construct.h” 文件中完成了。
以上就是各自文件需要处理的基本工作内容;
需要思考的问题:
如何向堆内存请求空间
;多线程环境下如何请求空间
;内存不足时需要如何处理
;请求过多 “小型区块” 空间,可能造成的内存碎片问题如何处理
;
SGI STL 空间配置器
STL 采用两级配置器:
- 第一级配置器直接使用 malloc() 和 free() 实现;
- 第二级配置器使用 memory pool 内存池管理(用来解决,小型区块可能造成内存碎片的问题)
分配规则:
- 当请求空间大小 > 128 bytes,使用第一级配置器(malloc() 和 free())
- 当请求空间大小 <= 128 bytes,使用第二级配置器(内存池管理)
(具体原理我们根据源代码具体分析)
源码分析
1.“stl_alloc.h”
第一级配置器 ( 使用 malloc() 和 free() 实现,比较简单 )
// 第一级配置器
// 该泛型类没有类型参数;
//“__inst”是一个写死的int类型,但无实际意义
template <int __inst>
class __malloc_alloc_template {
private:
//以下两个函数是,malloc 申请失败后(内存不足时)的处理方法
//用来申请空间,参数是申请的大小
static void* _S_oom_malloc(size_t);
//用来扩增一个旧的内存空间
//参数1,是旧的空间地址;参数2,是重新申请的大小
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)
{
// 调用 malloc()
void* __result = malloc(__n);
// 如果申请失败,改用 _S_oom_malloc()
if (0 == __result) __result = _S_oom_malloc(__n);
return __result;
}
//释放空间,参数1,地址指针;参数2,大小
//(很显然这里的第二参数没有意义)
// 调用 free()释放空间
static void deallocate(void* __p, size_t /* __n */)
{
free(__p);
//(为何__n 没有意义,c语言得知,free释放空间,是全部释放,不存在只释放一部分的情况)
}
//对一段旧空间扩容
static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz)
{
// 调用 realloc()
void* __result = realloc(__p, __new_sz);
// 如果申请失败,改用 _S_oom_malloc()
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 针对内存不足时的处理方法
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
// 默认为0
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;
//当 "内存不足处理方法" 并未被设置,便调用 __THROW_BAD_ALLOC,抛出异常信息
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);
}
}
simple_alloc:
//第二层包装,(一个简单的调用配置器的模板类)
//_Alloc可以选择调用的空间配置器(第一级或第二级)
//_Tp需要申请的对象的类型
template<class _Tp, class _Alloc>
class simple_alloc {
public:
// 申请n个_Tp类型的对象空间
static _Tp* allocate(size_t __n)
{ return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp)); }
//申请一个_Tp类型的对象空间
static _Tp* allocate(void)
{ return (_Tp*) _Alloc::allocate(sizeof (_Tp)); }
//释放_p的对象空间,
//(对于第一级配置器来讲__n没有实际意义)
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)); }
};
debug_alloc ,检测存储的数据是否被改变;
// 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.
// 把申请的内存空间首地址内容用于存放n的大小, 方便后面校验是否偏移
// 主要用于保护存储的数据不被随意改变
template <class _Alloc>
class debug_alloc {
private:
// _S_extra 的存在会导致,不会分配大小为0的内存空间
enum {_S_extra = 8}; // Size of space used to store size. Note
// that this must be large enough to preserve
// alignment.
public:
//多开辟了 _S_extra 大小的空间,用来存放申请的大小
static void* allocate(size_t __n)
{
char* __result = (char*)_Alloc::allocate(__n + (int) _S_extra);
*(size_t*)__result = __n; //首地址用来存放__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;
}
};
第二级配置器 ( 内存池管理 )
相对于第一级配置器,多了一些操作机制,其作用主要是避免太多小额区块造成内存的碎片。
其实小额区块带来的不仅是内存碎片,配置空间时的额外负担也是一个大问题,虽然额外负担永远无法避免,但是小额区块越小,额外负担所占的比例就越大,显得更加浪费。
分配规则:当请求空间大小 > 128 bytes,转到第一级配置器,否则就使用 _S_free_list
中的空间。
关键结构信息如下:
enum {_ALIGN = 8}; // 小型区块上调边界,即区块大小的增长倍数
enum {_MAX_BYTES = 128}; // 小区块的存储上限
enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN free-list 的个数
// free-list 的 node 节点结构,注意,这是一个联合体对象
union _Obj {
union _Obj* _M_free_list_link; // 利用联合体特点
char _M_client_data[1];
};
// 这是一个存放了16个 *_Obj 对象 的数组
// __STL_VOLATILE 是 “volatile”
static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];
其中 free-list 是指针数组(存放指针的数组),16 个数组元素,各自管理大小分别为 8, 16, 24, 32,…128 bytes(8 的倍数)的小额区块。
内存池节点信息:
static char* _S_start_free; // 内存池起始位置。只在 _S_chunk_alloc() 中变化
static char* _S_end_free; // 内存池结束位置。只在 _S_chunk_alloc() 中变化
static size_t _S_heap_size; // 在堆内存中已经申请的内存大小(累计大小)
如图空闲链表和内存池:
申请空间简单分析典型的三种情况,具体在源码中分析:
源码:
//没有模板类型参数
//参数1,用于多线程;参数2,无意义
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.
//这里如果使用 static const 去替代 enum ,设置数值大小更好
#if ! (defined(__SUNPRO_CC) || defined(__GNUC__))
enum {_ALIGN = 8}; // 小型区块的上调边界
enum {_MAX_BYTES = 128}; // 小区区块的上限
enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN free-list 的个数
# endif
// 向上舍入操作 ,这个计算方法可以学习一下
// 将请求的小额区块的大小调至 8 的倍数
static size_t
_S_round_up(size_t __bytes)
{ return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }
__PRIVATE:
// 注意:这里使用一个union,做了简单的内存优化
// 1.如果使用第一个成员, 表示指向另一个相同的 union 对象
// 2.如果使用第二个成员, 表示指向实际的内存区域
// 如此一来,一个结点使用相同的空间, 却能同时做两件事,索引和指向内存区域
//为什么要这样做呢?因为节点在使用和空闲时是两个状态,
// 使用时需要存放数据;空闲时需要将相同大小的内存块节点链接起来
// free-list 的节点结构,降低维护链表 list 带来的额外负担
union _Obj {
union _Obj* _M_free_list_link; // 利用联合体特点
char _M_client_data[1]; /* The client sees this. */
};
private:
# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
static _Obj* __STL_VOLATILE _S_free_list[];
// Specifying a size results in duplicate def for 4.1
# else
// 这是一个空闲列表,会将所有当前空闲(回收)的内存暂时保存起来,
// 数组元素中存放空闲区块链表
//使用前会全部初始化为0
static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];
# endif
//根据数据大小找到对应 _S_free_list 的下标,下标从 0 开始
static size_t _S_freelist_index(size_t __bytes) {
return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
}
// Returns an object of size __n, and optionally adds to size __n free list.
//当_S_free_list 对应下标的没有多余的空闲区块时调用
//向内存池申请 __n 个大小的空间,默认会要20个,但是真的给多少个内存池说了算
static void* _S_refill(size_t __n);
// Allocates a chunk for nobjs of size size. nobjs may be reduced
// if it is inconvenient to allocate the requested number.
//内存池分配空间
//参数1,申请__size 大小的空间
//参数2,参数1大小的空间 __nobjs 个
static char* _S_chunk_alloc(size_t __size, int& __nobjs);
// Chunk allocation state.
static char* _S_start_free; // 内存池起始位置。只在 _S_chunk_alloc() 中变化
static char* _S_end_free; // 内存池结束位置。只在 _S_chunk_alloc() 中变化
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.
//这里最好使用_STL_auto_lock。但我们不需要NULL检查。
//我们需要检测一下线程是否已经启动。
class _Lock;
friend class _Lock;
class _Lock {
public:
_Lock() { __NODE_ALLOCATOR_LOCK; }
~_Lock() { __NODE_ALLOCATOR_UNLOCK; }
};
public:
// 申请大小为n的数据块,返回该数据块的起始地址
static void* allocate(size_t __n)
{
void* __ret = 0;
// 申请的内存大小大于 128 就去第一空间配置器中申请内存
if (__n > (size_t) _MAX_BYTES) {
__ret = malloc_alloc::allocate(__n);
}
else {
//根据申请的空间大小获取对应的数组下标,得到我们将要获取的空闲区块链表首地址
//这里要注意,比如我们申请的是 7,但是不会真的给7,而是会给8,
//那么就意味着每次分配内存,只会多给,不会少给,
//所以在扩容的时候,可能从7扩容到8,但是实际处理不会再次给你分配内存,会直接给你返回
_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;
//__result == 0 说明没有内存块,首先将申请的大小调至 8 的倍数
//然后填充内存
if (__result == 0)
__ret = _S_refill(_S_round_up(__n));
else {
// 如果空闲链表中有空闲数据块,则取出一个,并把空闲链表的指针指向下一个空闲数据块
*__my_free_list = __result -> _M_free_list_link;
__ret = __result;
}
}
return __ret;
};
//回收函数
static void deallocate(void* __p, size_t __n)
{
if (__n > (size_t) _MAX_BYTES)
// 大于 128,调用第一级配置器的释放
malloc_alloc::deallocate(__p, __n);
else {
// 获取当前空闲列表中对应 __n 大小对应下标中存储的“空闲区块链表”头节点
_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;
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 */
/* We allocate memory in large chunks in order to avoid fragmenting */
/* the malloc heap too much. */
/* We assume that size is properly aligned. */
/* We hold the allocation lock. */
// 从内存池中申请空间
// 参数1,申请 __size 大小的空间内存
// 参数2,参数1那么大的空间内存,索要__nobjs个,但是具体给多少,还要看内存池,所以这是一个引用
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; // 计算内存池剩余空间
// 内存池剩余空间充足,全部给
if (__bytes_left >= __total_bytes) {
__result = _S_start_free; //返回这块空间首地址
_S_start_free += __total_bytes; //剩余空间的首地址指针需要偏移
return(__result);
} else if (__bytes_left >= __size) { // 内存池剩余空间不足提供__nobjs个,能给多少给多少
__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.
// 内存池的剩余空间,全部分给对应下标的空闲区块链表
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;
}
//调用malloc,扩容内存池
_S_start_free = (char*)malloc(__bytes_to_get);
if (0 == _S_start_free) { // 内存申请失败,堆内存空间不足
size_t __i;
_Obj* __STL_VOLATILE* __my_free_list;
_Obj* __p;
// Try to make do with what we have. That can't
// hurt. We do not try smaller requests, since that tends
// to result in disaster on multi-process machines.
//如果失败了,就去大于 __size 的空闲区块空间索取
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) {
//将空闲区块链表的首数据块回收,并将该空闲区块首地址偏移到下一位
*__my_free_list = __p -> _M_free_list_link;
_S_start_free = (char*)__p;
_S_end_free = _S_start_free + __i;
//然后重新去内存池分配并返回
return(_S_chunk_alloc(__size, __nobjs));
// Any leftover piece will eventually make it to the
// right free list.
}
}
//走到这里说明,去空闲链表回收也失败了,就调用第一级配置器
_S_end_free = 0; // In case of exception.
_S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);
// This should either throw an
// exception or remedy the situation. Thus we assume it
// succeeded.
}
//扩容内存池成功后,继续申请空间
_S_heap_size += __bytes_to_get;
_S_end_free = _S_start_free + __bytes_to_get;
return(_S_chunk_alloc(__size, __nobjs));
}
}
/* Returns an object of size __n, and optionally adds to size __n free list.*/
/* We assume that __n is properly aligned. */
/* We hold the allocation lock. */
//向内存池申请空闲空间,返回一个大小为__n的对象,
//如果申请较多区块,那么会为 _S_free_list 对应下标的空闲区块链表,新增节点
template <bool __threads, int __inst>
void*
__default_alloc_template<__threads, __inst>::_S_refill(size_t __n)
{
int __nobjs = 20;
// 调用 _S_chunk_alloc(),默认申请 20 个 __n 大小的区块
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);
// 超过1 ,需要将多余的加入到空闲列表中
// 根据申请数据块的大小找到数组下标,并定位了该数组元素(空闲区块链表头节点)
__my_free_list = _S_free_list + _S_freelist_index(__n);
/* Build free list in chunk */
__result = (_Obj*)__chunk;
// 这是一个连续的空间,所以将前 __n 返回给用户,
// 然后开始将后面的内存按照 __n的大小进行拆分成 (__nobjs - 1)个空闲区块节点并链接起来
*__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;
//新旧大小都大于 128 那么直接调用第一配置器扩容
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 */
2.“stl_construct.h”
调用对象的构造函数和析构函数:
#ifndef __SGI_STL_INTERNAL_CONSTRUCT_H
#define __SGI_STL_INTERNAL_CONSTRUCT_H
#include <new.h> // 使用 placement new,需要先包含此文件。
__STL_BEGIN_NAMESPACE
// construct and destroy. These functions are not part of the C++ standard,
// and are provided for backward compatibility with the HP STL. We also
// provide internal names _Construct and _Destroy that can be used within
// the library, so that standard-conforming pieces don't have to rely on
// non-standard extensions.
// Internal names
// 将初值 __value 设定到指针所指的空间上。
template <class _T1, class _T2>
inline void _Construct(_T1* __p, const _T2& __value) {
new ((void*) __p) _T1(__value); // placement new,调用 _T1::_T1(__value);
}
template <class _T1>
inline void _Construct(_T1* __p) {
new ((void*) __p) _T1();
}
// 对象指针调用对象的析构函数
template <class _Tp>
inline void _Destroy(_Tp* __pointer) {
__pointer->~_Tp();
}
// __first 和 __last 是两个迭代器
// 第三个参数如果是 __false_type,
// 以循环的方式遍历整个范围,并在循环中每经历一个对象就调用 destory()。
// non-trivial_destructor
template <class _ForwardIterator>
void
__destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type)
{
for ( ; __first != __last; ++__first)
destroy(&*__first);
}
// 第三个参数如果是 __true_type,什么都不做;
// trivial_destructor
template <class _ForwardIterator>
inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {}
//分析1:为什么用 __false_type 和 __true_type 区分调用 __destroy_aux() 函数
//在两个迭代器中一直调用 destroy 去销毁,如果很多都是一些无关痛痒的析构函数(trivial_destructor),
//那么这样循环调用析构函数,对效率来讲也是一种伤害。
//分析2:trivial_destructor和non-trivial_destructor的区别:
//如果用户不定义析构函数,而一直使用默认的析构函数,我们称之为 trivial_destructor
//如果用户定义了析构函数,在析构函数中做了一些事情,我们称之为 non-trivial_destructor
// 利用 _Trivial_destructor 判断调用哪个 __destroy_aux() 函数
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());
}
// 调用 __VALUE_TYPE() 获得迭代器所指对象的类型。
template <class _ForwardIterator>
inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) {
__destroy(__first, __last, __VALUE_TYPE(__first));
}
// destroy 泛型特化
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 */
// --------------------------------------------------
// Old names from the HP STL.
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);
}
template <class _Tp>
inline void destroy(_Tp* __pointer) {
_Destroy(__pointer);
}
// __first 和 __last 是两个迭代器,函数的意义是将 [__first, __last)范围内的所有对象析构掉。
template <class _ForwardIterator>
inline void destroy(_ForwardIterator __first, _ForwardIterator __last) {
_Destroy(__first, __last);
}
__STL_END_NAMESPACE
#endif /* __SGI_STL_INTERNAL_CONSTRUCT_H */
3.“stl_uninitialized.h”
STL 定义有五个全局函数,作用于未初始化的空间上。分别是:用于构造的函数(construct),用于析构的函数(destroy),uninitialized_copy(),uninitialized_fill(),uninitialized_fill_n()。前两个在 “stl_construct.h” 文件中,剩下三个在本文件中,但其中涉及很多STL算法函数 ---- copy(),fill(),fill_n()(这些在STL算法中详细介绍)。
#ifndef __SGI_STL_INTERNAL_UNINITIALIZED_H
#define __SGI_STL_INTERNAL_UNINITIALIZED_H
__STL_BEGIN_NAMESPACE
// uninitialized_copy 调用 copy constructor
// Valid if copy construction is equivalent to assignment, and if the
// destructor is trivial.
//对象是POD类型,底层调用 copy()函数
template <class _InputIter, class _ForwardIter>
inline _ForwardIter
__uninitialized_copy_aux(_InputIter __first, _InputIter __last,
_ForwardIter __result,
__true_type)
{
return copy(__first, __last, __result);
}
//对象不是POD类型,循环调用构造函数 _Construct
template <class _InputIter, class _ForwardIter>
_ForwardIter
__uninitialized_copy_aux(_InputIter __first, _InputIter __last,
_ForwardIter __result,
__false_type)
{
_ForwardIter __cur = __result;
// __STL_TRY 是 try
__STL_TRY {
for ( ; __first != __last; ++__first, ++__cur)
_Construct(&*__cur, *__first);
return __cur;
}
// __STL_UNWIND 是 catch ,这里是发生异常后,将销毁 __result
__STL_UNWIND(_Destroy(__result, __cur));
// 使用 __STL_TRY 和 __STL_UNWIND 的意义:
//要么建构出所有元素,要么不建构任何东西,不能存在半建构状态。
}
//判断对象(输出的迭代器)是否是POD类型,如果是第四个参数传:__true_type,如果不是传:__false_type
//何为POD?
// POD(Plain Old Data),指标量型别还是传统的C Struct型别
// POD类型 必须拥有trivial(默认) ctor/dtor/copy/assignment函数
template <class _InputIter, class _ForwardIter, class _Tp>
inline _ForwardIter
__uninitialized_copy(_InputIter __first, _InputIter __last,
_ForwardIter __result, _Tp*)
{
typedef typename __type_traits<_Tp>::is_POD_type _Is_POD;
return __uninitialized_copy_aux(__first, __last, __result, _Is_POD());
}
// 根据 __result 类型不同,调用不同的 __uninitialized_copy 函数
// 函数意义:把从 __first 到 __last迭代器中指向的数据,复制给以 __result 为开始指向的迭代器。
// [__first, __last)
template <class _InputIter, class _ForwardIter>
inline _ForwardIter
uninitialized_copy(_InputIter __first, _InputIter __last,
_ForwardIter __result)
{
// __VALUE_TYPE 是“stl_iterator_base.h”一个模板函数,萃取迭代器指向类型,传递不同类型调用不同函数
//函数返回值是一个类型指针 (这里分析输出的迭代器)
//template <class _Iter>
/*inline typename iterator_traits<_Iter>::value_type*
__value_type(const _Iter&)
{
return static_cast<typename iterator_traits<_Iter>::value_type*>(0);
}*/
return __uninitialized_copy(__first, __last, __result,
__VALUE_TYPE(__result));
}
inline char* uninitialized_copy(const char* __first, const char* __last,
char* __result) {
memmove(__result, __first, __last - __first);
return __result + (__last - __first);
}
// wchar_t 类型就是 unsigned short 类型;
inline wchar_t*
uninitialized_copy(const wchar_t* __first, const wchar_t* __last,
wchar_t* __result)
{
memmove(__result, __first, sizeof(wchar_t) * (__last - __first));
return __result + (__last - __first);
}
// uninitialized_copy_n (not part of the C++ standard)
//输入迭代器,循环调用 构造函数_Construct
template <class _InputIter, class _Size, class _ForwardIter>
pair<_InputIter, _ForwardIter>
__uninitialized_copy_n(_InputIter __first, _Size __count,
_ForwardIter __result,
input_iterator_tag)
{
_ForwardIter __cur = __result;
__STL_TRY {
for ( ; __count > 0 ; --__count, ++__first, ++__cur)
_Construct(&*__cur, *__first);
return pair<_InputIter, _ForwardIter>(__first, __cur);
}
__STL_UNWIND(_Destroy(__result, __cur));
}
//随机访问迭代器
template <class _RandomAccessIter, class _Size, class _ForwardIter>
inline pair<_RandomAccessIter, _ForwardIter>
__uninitialized_copy_n(_RandomAccessIter __first, _Size __count,
_ForwardIter __result,
random_access_iterator_tag) {
_RandomAccessIter __last = __first + __count;
return pair<_RandomAccessIter, _ForwardIter>(
__last,
uninitialized_copy(__first, __last, __result));
}
//原理同 uninitialized_copy_n 函数
template <class _InputIter, class _Size, class _ForwardIter>
inline pair<_InputIter, _ForwardIter>
__uninitialized_copy_n(_InputIter __first, _Size __count,
_ForwardIter __result) {
return __uninitialized_copy_n(__first, __count, __result,
__ITERATOR_CATEGORY(__first));
}
//备注:五种迭代器类型
//1.输入迭代器
//struct input_iterator_tag{};
//2.输出迭代器
//struct output_iterator_tag{};
//3.向前迭代器(继承自输入迭代器)
//struct forward_iterator_tag: input_iterator_tag{};
//4.双向迭代器(继承自向前迭代器)
//struct bidirectional_iterator_tag: forward_iterator_tag{};
//5.随机访问迭代器(继承自双向迭代器)
//struct random_access_iterator_tag: bidirectional_iterator_tag{};
//分析:后面三种迭代器3-5类型,都会进行隐式类型转换,转换为input_ierator_tag。
// 根据 __first 类型不同,调用不同的 __uninitialized_copy_n 函数
// 函数意义:把从 __first 开始__count个迭代器中指向的数据,复制给以 __result 为开始指向的迭代器。
// 利用 __ITERATOR_CATEGORY 函数获取 __first 不同类型(标签)的迭代器(这里分析输入的迭代器)
// 不同的迭代器的类型,会针对其有特定的方法实现
template <class _InputIter, class _Size, class _ForwardIter>
inline pair<_InputIter, _ForwardIter>
uninitialized_copy_n(_InputIter __first, _Size __count,
_ForwardIter __result) {
return __uninitialized_copy_n(__first, __count, __result,
__ITERATOR_CATEGORY(__first));
}
// Valid if copy construction is equivalent to assignment, and if the
// destructor is trivial.
//是POD 类型的fill 处理方法
template <class _ForwardIter, class _Tp>
inline void
__uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last,
const _Tp& __x, __true_type)
{
fill(__first, __last, __x);
}
//不是 POD类型的fill 处理方法
template <class _ForwardIter, class _Tp>
void
__uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last,
const _Tp& __x, __false_type)
{
_ForwardIter __cur = __first;
__STL_TRY {
for ( ; __cur != __last; ++__cur)
_Construct(&*__cur, __x);
}
__STL_UNWIND(_Destroy(__first, __cur));
}
//判断输出的迭代器是否是 POD类型
template <class _ForwardIter, class _Tp, class _Tp1>
inline void __uninitialized_fill(_ForwardIter __first,
_ForwardIter __last, const _Tp& __x, _Tp1*)
{
typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD;
__uninitialized_fill_aux(__first, __last, __x, _Is_POD());
}
//函数意义:使用 __x去填充迭代器从 __first 到 __last 中的数据 [__first, __last)
// 利用 __VALUE_TYPE 分析输出的迭代器类型,然后调用不同的最优解(函数)
template <class _ForwardIter, class _Tp>
inline void uninitialized_fill(_ForwardIter __first,
_ForwardIter __last,
const _Tp& __x)
{
__uninitialized_fill(__first, __last, __x, __VALUE_TYPE(__first));
}
// Valid if copy construction is equivalent to assignment, and if the
// destructor is trivial.
template <class _ForwardIter, class _Size, class _Tp>
inline _ForwardIter
__uninitialized_fill_n_aux(_ForwardIter __first, _Size __n,
const _Tp& __x, __true_type)
{
return fill_n(__first, __n, __x);
}
template <class _ForwardIter, class _Size, class _Tp>
_ForwardIter
__uninitialized_fill_n_aux(_ForwardIter __first, _Size __n,
const _Tp& __x, __false_type)
{
_ForwardIter __cur = __first;
__STL_TRY {
for ( ; __n > 0; --__n, ++__cur)
_Construct(&*__cur, __x);
return __cur;
}
__STL_UNWIND(_Destroy(__first, __cur));
}
//判断输出的迭代器是否是 POD 类型
template <class _ForwardIter, class _Size, class _Tp, class _Tp1>
inline _ForwardIter
__uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp& __x, _Tp1*)
{
typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD;
return __uninitialized_fill_n_aux(__first, __n, __x, _Is_POD());
}
// 函数意义:把从 __first 开始__n个迭代器中指向的数据,使用 __x 填充。
// __VALUE_TYPE 分析输出的迭代器类型
template <class _ForwardIter, class _Size, class _Tp>
inline _ForwardIter
uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp& __x)
{
return __uninitialized_fill_n(__first, __n, __x, __VALUE_TYPE(__first));
}
// Extensions: __uninitialized_copy_copy, __uninitialized_copy_fill,
// __uninitialized_fill_copy.
// __uninitialized_copy_copy
// 拷贝[first1, last1)到[result, result + (last1 - first1))
// 同时拷贝[first2, last2)到
// [result + (last1 - first1), result + (last1 - first1) + (last2 - first2)]
template <class _InputIter1, class _InputIter2, class _ForwardIter>
inline _ForwardIter
__uninitialized_copy_copy(_InputIter1 __first1, _InputIter1 __last1,
_InputIter2 __first2, _InputIter2 __last2,
_ForwardIter __result)
{
_ForwardIter __mid = uninitialized_copy(__first1, __last1, __result);
__STL_TRY {
return uninitialized_copy(__first2, __last2, __mid);
}
__STL_UNWIND(_Destroy(__result, __mid));
}
// __uninitialized_fill_copy
// 用x填充[result, mid),同时拷贝[first, last)到[mid, mid + (last - first))
template <class _ForwardIter, class _Tp, class _InputIter>
inline _ForwardIter
__uninitialized_fill_copy(_ForwardIter __result, _ForwardIter __mid,
const _Tp& __x,
_InputIter __first, _InputIter __last)
{
uninitialized_fill(__result, __mid, __x);
__STL_TRY {
return uninitialized_copy(__first, __last, __mid);
}
__STL_UNWIND(_Destroy(__result, __mid));
}
// __uninitialized_copy_fill
// 拷贝[first1, last1)到[first2, first2 + (last1 - first1))
// 并且用x填充[first2 + (last1 - first1), last2)
template <class _InputIter, class _ForwardIter, class _Tp>
inline void
__uninitialized_copy_fill(_InputIter __first1, _InputIter __last1,
_ForwardIter __first2, _ForwardIter __last2,
const _Tp& __x)
{
_ForwardIter __mid2 = uninitialized_copy(__first1, __last1, __first2);
__STL_TRY {
uninitialized_fill(__mid2, __last2, __x);
}
__STL_UNWIND(_Destroy(__first2, __mid2));
}
__STL_END_NAMESPACE
#endif /* __SGI_STL_INTERNAL_UNINITIALIZED_H */
4.“defalloc.h”
上面介绍SGI STL 的配置器叫alloc,但是它也提供了一个标准的allocator,但SGI 并未使用,也不建议我们使用,它只是基层内存配置/释放行为(::operator::new 和 ::operator::delete)的一层薄薄的包装,并没有考虑到任何效率上的强化,只是做了一个兼容。
/* 这是原始的 HP default allocator,提供它只是为了回溯兼容。 */
#ifndef DEFALLOC_H
#define DEFALLOC_H
#include <new.h>
#include <stddef.h>
#include <stdlib.h>
#include <limits.h>
#include <iostream.h>
#include <algobase.h>
template <class T>
inline T* allocate(ptrdiff_t size, T*) {
set_new_handler(0);
// 申请了 (size * sizeof(T)) 大小的空间
T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
if (tmp == 0) {
cerr << "out of memory" << endl;
exit(1);
}
return tmp;
}
template <class T>
inline void deallocate(T* buffer) {
::operator delete(buffer);
}
template <class T>
class allocator {
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
pointer allocate(size_type n) {
return ::allocate((difference_type)n, (pointer)0);
}
void deallocate(pointer p) { ::deallocate(p); }
pointer address(reference x) { return (pointer)&x; }
const_pointer const_address(const_reference x) {
return (const_pointer)&x;
}
size_type init_page_size() {
return max(size_type(1), size_type(4096/sizeof(T)));
}
size_type max_size() const {
return max(size_type(1), size_type(UINT_MAX/sizeof(T)));
}
};
class allocator<void> {
public:
typedef void* pointer;
};
#endif
备注:后期源码会上传