定义:空间配置器就是分配空间的操作的组件.
先介绍两个接口函数
设计一个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(直接移动内存内容)来执行复制行为,更加高效.
每天进步一点点!!!