2.1 空间配置器的标准接口

allocator的必要接口:

allocator::value_type
allocator::pointer
allocator::const_pointer
allocator::reference
allocator::const_reference
allocator::size_type
allocator::difference_type//一个嵌套的class template(类模板),
//class rebind<U>拥有唯一成员other(一个typedef,代表allocator<U>)
allocator::rebind//默认的构造函数
allocator::allocator()//copy constructor(复制构造器)
allocator::allocator(const allocator&)//泛化的copy constructor
template <class U>allocator::allocator(const allocator<U>&)//析构函数
allocator::~allocator()//返回某个对象的地址。算式a.address(x)等同于&x
pointer allocator::address(reference x) cosnt//返回某个cosnt对象的地址。算式a.address(x)等同于&x
const_pointer allocator::address(const_reference x) const//配置空间,足以存储n个T对象。
//第二个参数是提示。实现上可能会利用它来增进区域性(locality),或完全忽略之
pointer allocator::allocate(size_type n,const void*=0)//归还先前配置的空间
void allocator::deallocate(pointer p,size_type n)//返回可成功配置的最大量
size_type allocator::max_size() const//等同于new((void*) p) T(x)
void allocator::construct(pointer p,const T& x)//等同于p->~T()
void allocator::destroy(pointer p)


2.2 具备次配置力(sub-allocation)的SGI空间配置器

SGI STL的配置器名称是alloc,而非allocator,与标准规范不同,且不接受任何参数。

2.2.1 SGI标准的空间配置器,std::allocator

SGI中也定义有一个符合部分标准,名为allocator的配置器,但该配置器没有被全面使用,而只是把C++的::operator new和::operator delete做一层薄薄的包装而已。

2.2.2 SGI特殊的空间配置器,std::alloc

配置器定义于<memory>中,SGI <memory>内包含:

//定义了全局函数construct()和destroy(),负责对象的构造和析构
#include <stl_construct.h>//定义了一、二级配置器,彼此合作。配置器名为alloc。
#include <stl_alloc.h>//定义了一些用来填充(fill)或复制(copy)大块内存数据的函数,
//如:un_initialized_copy()
//        un_initialized_fill()
//        un_initialized_fill_n()
//这些函数不属于配置器的范畴,但与对象初值设置有关。
//对于容器的大规模元素初值设置很有帮助
//最差情况下,会调用construct(),
//最佳情况下,会使用C标准函数memmove()直接进行内存数据的移动。
#include <stl_uninitialized.h>

2.2.3 构造和析构基本工具:construct()和destroy()

/*<stl_construct.h>的部分代码 */
#include <new.h>    //欲使用placement new,需先包含此文件

template <class T1,class T2>
inline void construct(T1* p, const T2& value){new(p) T1(value);    //placemnt new; 调用T1::T1(value);
}//destroy()第一版本,接受一个指针
template <class T>
inline void destroy(T* pointer){pointer->~T();      //调用dtor ~T()
}//destroy()第二版本,接受两个迭代器,此函数设法找出元素的数字型别(value type),
//进而利用__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 first,ForwardIterator last,__true_type){}//destroy()第二版本针对迭代器为char*和wchar_t*的特化版
inline void destroy(char*,char*){}
inline void destroy(wchar_t*,wchar_t*){}

2.2.4 空间的配置与释放,std::alloc

对象构造前的空间配置和对象构造后的空间释放,由<stl_alloc.h>负责,SGI以malloc()和free()完成内存的配置和释放。

由于小型区块可能造成内存碎片问题,所以SGI设计了双层级配置器:

/*只开放第一级,还是同时开放第二级配置器,取决于__USE_MALLOC是否被定义*/
#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 *//**SGI STL 第一级配置器*1.allocate()直接使用malloc(),deallocate()直接使用free();*2.模拟C++的set_new_handler()以处理内存不足的状况。*/
template<int inst>
class __malloc_alloc_template { ... }/**SGI STL 第二级配置器*1.维护16个自由链表(free lists),负责16中小型区块的次配置能力,*   内存池(memory pool)以malloc()配置而得;*2.如果需求区块大于128bytes(内存不足),则转调用第一级配置器。*/
template <bool threads, int inst>
calss __default_alloc_template { ... }

SGI还为配置器(无论第一还是第二级)包装了一个能够符合STL 规格的接口:

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)); }
};

2.2.7 空间配置函数allocate()

__default_alloc_template拥有配置器的标准接口函数allocate()。

//n must be > 0
static void * allocate(size_t n){obj * volatile * my_free_list;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 listvoid *r=refill(ROUND_UP(n));return r;}//调整free list*my_free_list=result->free_list_link;return (result);
}

2.2.8 空间释放函数deallocate()

__default_alloc_template拥有配置器的标准接口函数deallocate()。

//p 不可以是0
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 listmy_free_list=free_list+FREELIST_INDEX(n);//调整free list,回收区块q->free_list_link=*my_free_list;*my_free_list=q;
}

2.2.9 重新填充free lists

当allocate()函数发现free list中没有可用区块了时,就调用refill(),准备为free list重新填充空间。

新的空间将取自内存池(经由chunk_alloc()完成)。

缺省取得20个新节点(新区块),但万一内存池空间不足,获得的节点数(区块数)可能小于20:

//返回一个大小为n的对象,并且有时候会为适当的free list增加节点
//假设n已经适当上调至8的倍数
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 REFERENCEchar * chunk=chunk_alloc(n, nobjs);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空间内建立free listresult=(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);
}

2.2.10 内存池(memory pool)

上一小节提到的chunk_alloc()函数以end_free – start_free来判断内存池的水量。

如果水量充足,就直接调出20个区块返回给free list。

如果水量不足以提供20个区块,但还足够供应一个(含)以上的区块,就拨出这不足20个区块的空间出去。这时候其pass by reference的nobjs参数将被修改为实际能够供应的区块数。

如果内存池连一个区块空间都无法供应,便需利用malloc()从heap中配置内存,为内存池注入源头活水以应付需求。

新水量的大小为需求量的两倍,再加上一个随着配置次数增加而愈来愈大的附加量。

若整个system heap 空间都不够(以至无法为内存池注入源头活水),则malloc()失败,chunk_alloc()就四处寻找有无“尚有为用区块,且区块够大”的free lists。找到了就挖一块交出,找不到就调用第一级配置器。

第一级配置器其实也是使用malloc()来配置内存,但它有out-of-memory处理机制,或许有机会释放其他内存拿来此处使用。如果可以,就成功,否则发出bad_alloc异常。


2.3 内存基本处理工具

STL作用于未初始化空间上的五个全局函数——用于构造的construct()、用于析构的destroy(),以及uninitialized_copy(),uninitialized_fill(),uninitialized_fill_n()分别对应高层次函数copy()、fill()、fill_n()。

2.3.1 uninitialized_copy

uninitialized_copy()使我们能够将内存的配置与对象的构造行为分离开来,

如果作为输出目的地的[result,result+(last-first)) 范围内的每一个迭代器都指向未初始化区域,

则uninitialized_copy()会使用copy constructor,给身为输入来源之[first, last)范围内的每一个对象产生一份复制品,放进输出范围中。

2.3.2 uninitialized_fill

uninitialized_fill()使我们能够将内存的配置与对象的构造行为分离开来,

如果[first, last)范围内的每个迭代器都指向未初始化的内存,

那么uninitialized_fill()会在该范围内产生x(该函数的第三个参数 const T& x)的复制品。

2.3.3 uninitialized_fill_n

uninitialized_fill()使我们能够将内存的配置与对象的构造行为分离开来。

它会为指定范围内的所有元素设定相同的初值。

如果[first, first+n)范围内的每一个迭代器都指向未初始化的内存,

那么uninitialized_fill_n()会调用copy constructor,在该范围内产生x(该函数的第三个参数 const T& x)的复制品。

这三个函数的进行逻辑是,萃取出迭代器first(uninitialized_copy函数是取出result)的value type,然后判断该型别是否为POD型别:

—— 如果是POD型别,则执行其对应的高层次函数(分别是copy(),fill(),fill_n());

——如果不是,则只能一个一个元素地构造(遍历每个迭代器,调用construct()),无法批量进行。

*POD——Plain Old Data,即标量型别(scalar types)或传统的C struct型别

——故POD型别必然拥有trivial ctor/tor/copy/assignment函数

——因此可以对POD型别采用最有效率的初值填写手法,而对非POD型别只好采用最保险安全的做法。

转载于:https://www.cnblogs.com/atmacmer/p/6279598.html

Ch2 空间配置器(allocator) ---笔记相关推荐

  1. C++ 空间配置器(allocator)

    C++ 空间配置器(allocator) 在STL中,Memory Allocator 处于最底层的位置,为一切的 Container 提供存储服务,是一切其他组件的基石.对于一般使用 STL 的用户 ...

  2. STL_空间配置器allocator

    空间配置器 所有的STL的操作对象(所有的数值)都存放在容器内,而容器一定要配置空间才能存放资料 空间配置器的标准接口 typedef unsigned int size_t: allocator:: ...

  3. 一篇文章搞懂STL中的空间配置器allocator(原创,多图,易懂)

    Table of Contents 0.引入 1.标准的空间配置器allocator 2.更为高效的空间配置器alloc 2.1----对象的构造与析构 2.1.1 对象的构造:::construct ...

  4. 【《STL源码剖析》提炼总结】 第1节:空间配置器 allocator

    文章目录 一. 什么是空间配置器 二. STL allocator的四个操作: allocate,deallocate,construct,destroy `construct()` `destroy ...

  5. STL笔记(二)---空间配置器

    一.概述 allocator是STL的六大组件之一,空间配置器.其作用就是为各个容器管理内存(内存开辟 内存回收).allocator配置的对象不只是内存,它也可以向硬盘索取空间.使用STL库的时候不 ...

  6. 复习SGI STL二级空间配置器(内存池) | 笔记自用

    前言 在以前学习C++的时,写过一些剖析STL空间配置器的文章,如今回头再看一遍,想着复习一下. SGI STL空间配置器: [该目录中查看] SGI STL包含了一级空间配置器和二级空间配置器,其中 ...

  7. 《STL源码剖析》读书笔——(1)空间配置器

    第二章 空间配置器(allocator) 空间配置器按我的理解就是C++ STL进行内存管理的组件(包括内存的申请和释放):当然,不只是内存,还可以向硬盘申请空间: 我主要看了内存的配置与释放(这里& ...

  8. 深度剖析SGI STL二级空间配置器内存池源码

    文章目录 一.SGI STL二级空间配置器重要成员解读 二. 二级空间配置器内存池的结构 三. 两个重要的函数 1. _S_round_up 2. _S_freelist_index 四. 内存池al ...

  9. 489-剖析SGI STL空间配置器

    引言 不管是C的malloc free 还是C++的new delete,底层涉及内存管理调用的都是malloc,free,malloc和free是C的库函数. 如果我们在应用场景中涉及小块内存的开辟 ...

最新文章

  1. STS Eclipse IDEA 指定启动JDK版本
  2. C++ 继承Employee类
  3. LeetCode 98验证二叉搜素树(中序遍历)99恢复二叉搜索树
  4. jquery获取select选择的显示值
  5. api 原生hbase_hbase之java api实战一
  6. C 语言 结构类型 结构
  7. k近邻法 kd树 平衡kd树
  8. 三角函数π/2转化_三角函数不会做?看这里,带你搞定
  9. Log4j2 Demos(基础/时间大小回滚/定期删除/日志脱敏)
  10. 胎儿产科生长发育曲线 ,体重估计,公式和绘图、参考文献、python代码
  11. 【Java】用java程序求李白的酒
  12. ESP-iSYS数据库(实时数据库),API使用
  13. 健身环1536级小结:相当适合码农的锻炼方式
  14. java 过滤字符串_java 过滤字符串方法实现
  15. React组件Component
  16. 人脸识别准确概率计算——超详细
  17. nm and n||m 的区别
  18. 生物发光及化学发光的原理及其应用
  19. stream流去除对象的值_I/O流(过滤流、对象序列化、字符流)
  20. sublime text 使用简单说明

热门文章

  1. linux运维相关操作(centos/Ubuntu)
  2. 第五章项目 体检套餐管理系统
  3. 自适应/响应式网页设计
  4. Adb connection Error:远程主机强迫关闭了一个现有的连接
  5. Spring的配置文件详解
  6. v8学习笔记(八) 【JS与C++互调】
  7. Linux中硬盘转速查看
  8. 分布式离线计算—HiveSQL
  9. python 全局变量引用与修改
  10. 如何实现一套可切换的声网+阿里的直播引擎