STL源码剖析--空间配置器心得笔记

定义:空间配置器就是分配空间的操作的组件.

 

先介绍两个接口函数

设计一个STL的空间配置器有有一些必要的接口,这里就不一一列举,列举两个在书中的全局函数

第一个是constuct()负责构造用的.

template <class T1, class T2>
inline void _construct(T1* p, const T2& value){
    new(p) T1(value);
}

上面代码是_construct(),因为constuct()对_construst()做了包装.

new(p) T1(value)这里是placement new.需要包含头文件#include<new> 相当于p指向的内存空间创建一个T1对象,然后把T2强转给T1,会调用T1的构造函数.

优点:相当于把已有空间当成缓冲区,减少分配空间耗费时间,因为直接new去找空闲空间位置比较耗费时间.

 

第二个是destroy()负责析构用的.

对于destroy有两个重载版本,一个是对一个destory,则直接调用析构函数,另外一个是对一段destory,不会直接调用析构函数,而是先取得type_value,

再利用__type_traits<T>判断此型别是否无关痛痒,若返回__ture_type则说明无关痛痒,什么也不做,若返回__flase_type则必须调用循环将一段对象析构.

因为如果范围很大且都无关痛痒,这样就节省了时间.

 

两个空间配置器

SGI STL中包含两个空间配置器:

第一个是std::allocator,但这个效率不佳,只是对::operator new和::operator delete做一层薄薄的包装.

这里不做详细说明.

第二个是std::alloc,是SGI STL默认的空间配置器,其中实现采用了内存池,对于分配大量小容量的对象,可以大大减少内存碎片。

下面做详细解释.

 

std::alloc中又含有有第一级配置器和第二级配置器.默认是第二级配置器,是通过预处理语句#ifdef __USE_MALLOC确定的,__USE_MALLOC未定义.

无论是第一级还是第二级空间配置器,SGI STL定义了一个simple_alloc对外接口包装了一下.

 

第一级配置器(__malloc_alloc_template)

第一级配置器是使用malloc(),free(),realloc()等c函数执行实际的内存配置,释放,重配置的操作.也实现了类似于c++中的new-handler机制.

new-handle机制是指,若需求的内存配置无法满足时会不断地调用一个你指定的函数new-handle,当满足内存仍然不满足需求或者未设定"内存不足处理例程"时,

将抛出bad_alloc异常.

 

第二级配置器(__default_alloc_template)

若当前区块大于128bytes时,调用第一级,反之调用内存池管理.这样的优点是避免了太多小额区块造成内存的碎片,增加配置时的额外负担.

内存池是实现机制是维护16个free-lists,各自管理的大小是其8的倍数,free-lists的实质是链表,这个链表是由union定义的,这样做的好处是,不会为了维护链表所必须的指针而造成内存的浪费.

 

内存池的实现

主要介绍有三个函数:

空间配置函数allocate()

当发现大于128则调用第一级配置器的allocate,否则看有无可用的free-lists,如果有就用一个类似于链表的删除操作,分配内存.因为是从free-lists中拿内存分配嘛.否则就调用refill().

空间释放函数deallocate()

当发现大于128则调用第一级配置器deallocate,否则就用一个类似于链表的插入操作,将区块回收.

重新填充函数

前面提到的refill()函数调用chunk_alloc(),chunk_alloc默认从内存池中取出20大小的节点.里面的机制大概是,如果内存池够,则调出20个区块,如果不够,且至少能满足一个,则返回一个.
如果完全没有则需要利用malloc从heap中获取内存.如果整个heap也没有内存,则去四处去其他大于当前等级的free-lists找尚未用区块.找到了就用,否则去第一级配置器找,虽然第一级配置器也是用malloc分配内存的但是其中有set-new-handle机制.

 

最后介绍了五个内存处理工具

前面两个constuct和destory已经讲过了.

剩下三个都是对为初始化区域初值填写,只不过区域的描述不同,都具备"commit or rollback"语意,意思是要么构造全部,如果有一个失败则都不构造.

下面是详细对比:

 

1.uninitialized_fill_n(first,n,x) first到first+n-1填写x

先用__type_traits<T1>::is_POD_type判断是否为POD型别,POD型别是指必然拥有trivialctor/dtor/copy/assignment函数.所以可以用一些高效的初值填写操作,如果是__true_type,则调用fill_n(),否则一一调用construct().

 

2.uninitialized_fill(first,last,x) first到last-1填写x

先用__type_traits<T1>::is_POD_type判断是否为POD型别,如果是__true_type,则调用fill(),否则一一调用construct().

 

3.uninitialized_copy(first,last,result)将result到result后面last-first-1的赋初值为[first,last)的对象

先用__type_traits<T1>::is_POD_type判断result是否为POD型别,如果是__true_type,则调用copy(),否则一一调用construct().

uninitialized_copy针对char*和wchar*有两个特化版本,采用memmove(直接移动内存内容)来执行复制行为,更加高效.

 

每天进步一点点!!!

 

posted @ 2020-12-05 20:05  Ldler  Views(117)  Comments(0Edit  收藏  举报