stl_construct.h
construct()
#include <new.h> //欲使用placement new,需先含入此文件
template <class T1, class T2>
inline void construct(T1* p, const T2& value) {
new (p) T1(value) ; //placement new; 唤起 T1::T1(value);
}
//以下是 destroy() 第一版ᴀ,接受一个指标。
template <class T>
inline void destroy(T* pointer) {
}
pointer->~T() ; //唤起 dtor ~T()
//以下是 destroy() 第二版ᴀ,接受两个迭代器。此函式设法找出元素的数值型别,
//进而利用 __type_traits<>求取最适当措施。
template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last) {
__destroy(first, last, value_type(first));
}
//判断元素的数值型别( value type)是否有trivial destructor
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());
}
//如果元素的数值型别( value type)有non-trivial destructor…
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
for ( ; first < last; ++first)
destroy(&*first);
}
//如果元素的数值型别( value type)有trivial destructor…
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}
//以下是 destroy() 第二版ᴀ针对迭代器为 char*和 wchar_t*的特化版
inline void destroy(char*, char*) {}
inline void destroy(wchar_t*, wchar_t*) {}
函数
construct()
将value 赋值到p指向的空间中
destory()
当参数为1个指针
直接调用析构函数
当参数为为[first,last)一段空间
由于范围有可能很大,如果大量掉用trivial destructor(默认,没什么乱用析构函数)会花费很大的开销,所以只调用non-trivial destructor。
方法是:通过value_type()获取元素类型,进一步判断析构类型作为参数,最终重载_destroy_aux()实现上述过程char*,wcahr_t*两个特化
啥都不干,相当于_destroy_aux()对trivial destructor的操作,对于单个不调用~()的特例。
stl_alloc.h
一级适配器 二级适配器
一级配置器直接使用 malloc() 和free() ,第二级配置器则视情况采用不同的策略:
当配置区块超过128bytes,视之为「足够大」, 便呼叫第一级配置器;当配置区
块小于 128bytes,视之为「过小」, 为了降低额外负担
便采用复杂的memory pool整理方式,而不再求助于第一级配置器。
判断
# ifdef __USE_MALLOC
...
typedef __malloc_alloc_template<0> malloc_alloc;
typedef malloc_alloc alloc;
//令 alloc为第一级配置器
# else
...
//令 alloc 为第二级配置器
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
#endif /* ! __USE_MALLOC */
通过USE MALLOC 判断使用一级还是二级适配器 (alloc)
simple alloc
template<class T, class Alloc>
class simple_alloc {
public:
static T *allocate(size_t n)
{ return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); }
static T *allocate(void)
{ return (T*) Alloc::allocate(sizeof (T)); }
static void deallocate(T *p, size_t n)
{ if (0 != n) Alloc::deallocate(p, n * sizeof (T)); }
static void deallocate(T *p)
{ Alloc::deallocate(p, sizeof (T)); }
};
包装alloc,使其具有标准接口
一级适配器(抄书)
template <int inst>
class __malloc_alloc_template {
private:
//以下都是函式指标,所代表的函式将用来处理内存不足的情况。
// oom : out of memory.
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); //第一级配置器直接使用 malloc()
// 以下,无法满足需求时,改用 oom_malloc()
if (0 == result) result = oom_malloc(n);
return result;
}
static void deallocate(void *p, size_t /* n */)
{
free(p); //第一级配置器直接使用 free()
}
static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)
{
void * result =realloc(p, new_sz); //第一级配置器直接使用 realloc()
// 以下,无法满足需求时,改用 oom_realloc()
if (0 == result) result = oom_realloc(p, new_sz);
return result;
}
//以下模拟 C++的 set_new_handler(). 换句话说,你可以透过它,
//指定你自己的 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
//初值为 0。有待客端设定。
template <int inst>
The Annotated STL Sources
58 第 2 章空间配置器( allocator)
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);
}
}
//注意,以下直接将参数 inst指定为 0。
typedef __malloc_alloc_template<0> malloc_alloc;
函数
首先是一级配置器的allocate() 和realloc() 都是在使用malloc()
和realloc() ,如果失败的话 调用oom_malloc() 和oom_realloc() 。 两个内循环函数
set_malloc_handler() 是模拟set_new_handler() 对于内存不足的处理
二级适配器(抄书)
enum {__ALIGN = 8}; //小型区块的上调边界
enum {__MAX_BYTES = 128}; //小型区块的上限
enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; //free-lists个数
//以下是第二级配置器。
//注意,无「template 型别参数」, 且第二参数完全没派上用场。
//第一参数用于多绪环境下。ᴀ书不讨论多绪环境。
template <bool threads, int inst>
class __default_alloc_template {
private:
// ROUND_UP() 将 bytes上调至 8的倍数。
static size_t ROUND_UP(size_t bytes) {
return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));
}
private:
61
unionobj { //free-lists 的节点构造
union obj * free_list_link;
char client_data[1]; /* The client sees this. */
};
private:
// 16 个 free-lists
static obj * volatilefree_list[__NFREELISTS];
// 以下函式根据区块大小,决定使用第 n号 free-list。 n 从 1 起算。
static size_t FREELIST_INDEX(size_t bytes) {
return (((bytes) + __ALIGN-1)/__ALIGN - 1);
}
The Annotated STL Sources
62 第 2 章空间配置器( allocator)
// 传回一个大小为 n的对象,并可能加入大小为 n的其它区块到free list.
static void *refill(size_t n);
// 配置一大块空间,可容纳 nobjs 个大小为 "size" 的区块。
// 如果配置 nobjs个区块有所不便, nobjs可能会降低。
static char *chunk_alloc(size_t size, int &nobjs);
// Chunk allocation state.
static char *start_free; //记忆池起始位置。只在 chunk_alloc() 中变化
static char *end_free; //记忆池结束位置。只在 chunk_alloc() 中变化
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);
};
//以下是 static data member 的定义与初值设定
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, };
对于大于128bytes 交给1级配置器处理,小于128bytes 用记忆池memory pool处理。块区大小使用实际大小向上取第一个2次幂数大小ROUND_UP()。
函数
- 空间配置函式allocate()
static void *allocate(size_t n)
{
obj * volatile * my_free_list;
The Annotated STL Sources
2.2具备次配置力( sub-allocation)的 SGI 空间配置器
obj * result;
// 大于 128 就呼叫第一级配置器
if (n > (size_t) __MAX_BYTES)
return(malloc_alloc::allocate(n));
}
// 寻找 16 个 free lists 中适当的一个
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
if (result == 0) {
// 没找到可用的 free list,准备重新填充 free list
63
void *r = refill(ROUND_UP(n));
return r;
}
// 调整 free list
//下节详述
*my_free_list = result -> free_list_link;
return (result);
};
此函式首先判断区块大小,大于 128 bytes 就呼叫第一级配置器,小
于 128 bytes 就检查对应的 free list。如果free list之内有可用的区块,就直接拿来
用,如果没有可用区块,就将区块大小上调至 8 倍数边界,然后呼叫refill() ,
准备为 free list 重新填充空间
2. 空间释还函式 deallocate()
跟空间配置函数一致
static void deallocate(void *p, size_t n)
{
obj *q = (obj *)p;
obj * volatile * my_free_list;
// 大于 128 就呼叫第一级配置器
if (n > (size_t) __MAX_BYTES) {
malloc_alloc::deallocate(p, n);
return;
}
// 寻找对应的 free list
my_free_list = free_list + FREELIST_INDEX(n);
// 调整 free list,回收区块
q -> free_list_link = *my_free_list;
*my_free_list = q;
}
-
3. 填充free-list空间的函数 refill
template <bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n)
{
int nobjs = 20;
// 呼叫 chunk_alloc() ,尝试取得 nobjs个区块做为 free list的新节点。
// 注意参数 nobjs是pass by reference。
char * chunk =chunk_alloc(n, nobjs); //下节详述
The Annotated STL Sources
66
obj * volatile * my_free_list;
obj * result;
obj * current_obj, * next_obj;
int i;
// 如果只获得一个区块,这个区块就拨给呼叫者用, free list无新节点。
if (1 == nobjs) return(chunk);
// 否则准备调整 free list,纳入新节点。
my_free_list = free_list + FREELIST_INDEX(n);
// 以下在 chunk空间内建立freelist
result = (obj *)chunk; //这一块准备传回给客端
// 以下导引 free list指向新配置的空间(取自记忆池)
*my_free_list = next_obj = (obj *)(chunk + n);
// 以下将 free list 的各节点串接起来。
for (i = 1; ; i++) {//从 1 开始,因为第 0 个将传回给客端
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);
}
-
4. chunk_alloc()
refill()调用的函数,从记忆池中取空间给 free_list用
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;
The Annotated STL Sources
return(result);
} else if (bytes_left >= size) {
67
// 记忆池剩余空间不能完全满足需求量,但足够供应一个(含)以上的区块。
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) {
// 记忆池内还有一些零头,先配给适当的 free list。
// 首先寻找适当的 free list。
obj * volatile * my_free_list =
free_list + FREELIST_INDEX(bytes_left);
// 调整 free list,将记忆池中的残余空间编入。
((obj *)start_free) -> free_list_link = *my_free_list;
*my_free_list = (obj *)start_free;
}
// 配置 heap 空间,用来挹注记忆池。
start_free = (char *) malloc(bytes_to_get);
if (0 == start_free) {
// heap 空间不足, malloc() 失败。
int i;
obj * volatile * my_free_list, *p;
// 试着检视我们手上拥有的东西。这不会造成伤害。我们不打算尝试配置
// 较小的区块,因为那在多行程( multi-process)机器上容易导致灾难
// 以下搜寻适当的 free list,
// 所谓适当是指「尚有用区块,且区块够大」之 free list。
for (i = size; i <= __MAX_BYTES; i += __ALIGN) {
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
if (0 != p) {//free list 内尚有用区块。
// 调整 free list以释出用区块
*my_free_list = p -> free_list_link;
start_free = (char *)p;
end_free = start_free + i;
// 递归呼叫自己,为了修正 nobjs。
return(chunk_alloc(size, nobjs));
// 注意,任何残余零头终将被编入适当的 free-list中备用。
}
}
end_free = 0; // 如果出现意外(山穷水尽,到处都没内存可用了)
// 呼叫第一级配置器,看看out-of-memory机制能否尽点力
start_free = (char *) malloc_alloc::allocate(bytes_to_get);
// 这会导致掷出异常(exception), 或内存不足的情况获得改善。
The Annotated STL Sources
68
}
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
// 递归呼叫自己,为了修正 nobjs。
return(chunk_alloc(size, nobjs));
}
}
首先判断memory pool中是否有足够的空间
能满足要求就直接给分配
不能满足进一步判断是否能供应一块以上,修改nobjs 给最大可能块
如果一块也无法供给,即
bytes_left<size
做如下操作
1把记忆池残存的不足那一块的加入 2malloc heap中的内存给memory pool
新开辟的是原来的两倍 2*total_bytes+ROUND_UP(heap_size>>4);
3最后malloc失败(heap不够) 的一些处理
stl_uninitialized
摘要
主要是这三个uninitialized_copy(),uninitialized_fill(),uninitialized_fill_n()
commit or rollback 要么做完,要么不作。就像文件传输中,如果有一个元素引出了异常,就要将先前产生的元素析构掉
另外这三个函数有相对应的stl三个高阶函数 copy() ,fill(),fill_n();在具体执行过程根据元素是否为POD判断后选择调用
POD(抄书)
POD意指 Plain Old Data,也就是纯量型别( scalar types)或传统的 C struct型别。
POD型别必然拥有 trivialctor/dtor/copy/assignment函式,因此,我们可以对
POD型别采取最有效率的初值填写手法,而对non-POD 型别采取最保险安全的
函数概述
对POD型别采取最有效率的初值填写手法,具体实现对应在高阶函数中,而对non-POD 型别采取最保险安全的作法。
高阶函数的具体实现第二章书中没有讲到,以下对函数的概述均为对于non-POD的操作
1. uninitialized_copy()
template <class InputIterator, class ForwardIterator>
ForwardIterator
uninitialized_copy(InputIterator first, InputIterator last,
ForwardIterator result);
此函数会,对区间内每一个元素,调用
construct(&*(result+(i-first)),*i)
向result(未初始化的内存的起址) 输出[first,last)区间
这里result是内存指针,[first,last)是迭代器区间
-
2. uninitialized_fill()
template <class ForwardIterator, class T>
void uninitialized_fill(ForwardIterator first, ForwardIterator last,
const T& x);
fill顾名思义为填充,即填充元素,此函数跟uninitialized_copy()功能类似,只是参数不同(可以说是反过来了)
这里[first,last) 范围内的每个迭代器都指向初始化的内存 ,x为值,在该范围内存内产生x的复制品。对于范围内的每一迭代器指向的内存。执行将x复制于内存中。
construct(&*i, x)
-
3. uninitialized_fill_n()
template <class ForwardIterator, class Size, class T>
ForwardIterator
uninitialized_fill_n(ForwardIterator first, Size n, const T& x);
跟第二个一样,只是区间表示为[fisrt,first+size)
函数实现(抄书)
- uninitialized_fill_n()
template <class ForwardIterator, class Size, class T>
inline ForwardIterator uninitialized_fill_n(ForwardIteratorfirst,
Size n, const T&x) {
return __uninitialized_fill_n(first, n, x, value_type(first) );
// 以上,利用 value_type() 取出 first的 value type.
}
这一段对通过value type()进行类型检测,在调用自己不同的重载
// destructor 是 trivial,以下就有效。
//如果是 POD型别,执行流程就会转进到以下函式。这是藉由 function template
//的自变量推导机制而得。
template <class ForwardIterator, class Size, class T>
inline ForwardIterator
__uninitialized_fill_n_aux(ForwardIterator first, Size n,
const T& x, __true_type) {
return fill_n(first, n, x); //交由高阶函式执行。见 6.4.2节。
}
// 如果不是 POD 型别,执行流程就会转进到以下函式。这是藉由 function template
//的自变量推导机制而得。
template <class ForwardIterator, class Size, class T>
ForwardIterator
__uninitialized_fill_n_aux(ForwardIterator first, Size n,
const T& x, __false_type) {
ForwardIterator cur = first;
// 为求阅读顺畅,以下将原ᴀ该有的异常处理( exception handling)省略。
for ( ; n > 0; --n, ++cur)
construct(&*cur, x);
return cur;
如果first类型为POD型,那么调用相对应的高阶函数处理,即
return fill_n(first, n, x);
如果不是POD型,那么通过循环 执行上述调用construct(&*i, x) ;
PS:省略了异常处理的解释
-
2. uninitialized_copy()
uninitialized_*三个函数中只有copy()有对char*和wchar*的特化如下
通过最具效率的memmove(直接搬移
内存内容)来执行复制行为。
inline char*uninitialized_copy(const char* first, const char* last,
char* result) {
memmove(result, first, last - first);
return result + (last - first);
}
//以下是针对 const wchar_t* 的特化版ᴀ
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);
}
如上类似 并不想缀述):
POD
return copy(first, last, result);
non-POD
for ( ; first != last; ++first, ++cur)
construct(&*cur, *first);
-
3. uninitialized_fill()
POD
fill(first, last, x);
non-POD
for ( ; cur != last; ++cur)
construct(&*cur, x);
完了:感觉其实概述和实现可以写在一起,重复了很多。不过书上是这么写的。就按这样来吧:)