STL源码剖析笔记(空间适配器)

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. 当参数为1个指针

      直接调用析构函数

    2. 当参数为为[first,last)一段空间

      由于范围有可能很大,如果大量掉用trivial destructor(默认,没什么乱用析构函数)会花费很大的开销,所以只调用non-trivial destructor。
      方法是:通过value_type()获取元素类型,进一步判断析构类型作为参数,最终重载_destroy_aux()实现上述过程

    3. 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
582 章空间配置器( 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
622 章空间配置器( 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()。

函数

  1. 空间配置函式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)

函数实现(抄书)

  1. 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);

完了:感觉其实概述和实现可以写在一起,重复了很多。不过书上是这么写的。就按这样来吧:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值