STL源码剖析--空间配置器

关于三种STL的历史和由来
http://www.360doc.com/content/05/1002/01/494_16409.shtml
以下介绍的是SGI STL提供的配置器(linux的gcc)
pj stl visual c++
STL六大组件
1.容器(containers):各种数据结构,vector,list,deque,set,map等
2.算法(algorithms):常用算法
sort,search,copy,erase…
3.迭代器(iterator):扮演容器与算法之间的胶合剂,是所谓的”泛型指针”
4.伪函数(functors):行为类似函数,可作为算法的某种策略
5.配接器(adapters):一种用来修饰容器或仿函数或迭代器接口的东西。
6.配置器(allocators):负责空间配置与管理

六大组件的相互关系:容器通过配置器取得数据存储空间,算法通过迭代器存取容器内容,为函数可以协助算法完成不同的策略变化,配接器可以修饰或套接伪函数。

空间配置器
整个STL的操作对象都存放在容器之内,而容器一定需要配置空间以置放资料。
需要注意的是allocator是空间配置器而不是内存配置器,因为空间不一定是内存,空间也可以是磁盘或其他辅助存储介质。(比如你可以写一个allocator直接向硬盘取空间)。

空间配置器的标准接口
allocator::value_type
allocator::pointer
allocator::const_pointer
allocator::reference
allocator::const_reference
allocator::size_type
allocator::difference_type
allocator::rebind
allocator::allocator()
allocator::allocator(const allocator&)
template < class U > allocator::allocator(const allocator< U >&)
allocator::~allocator()
pointer allocator::address(reference x) const
返回某个对象的地址,算法a.address(x)等同于&x
const_pointer allocator::address(const_reference x) const
返回某个const对象的地址
pointer allocator::allocate(size_type n,const void* = 0)
配置空间,足以存储n个T对象,第二个参数是个提示。实现上可能会利用它来增进区域性,或完全忽略之。
void allocator::deallocate(pointer p,size_type n)
归还先前配置的空间
size_type allocator::max_size() const
返回可成功配置的最大量
void allocator::construct(pointer p,const T& x)
等同于new((const void*)p) T(x)
void allocator::destroy(pointer p)
等同于p->~T()

一般情况下,我们所习惯的C++内存配置操作和释放操作是这样的

class Foo{...};
Foo* pf = new Foo;//配置内存,然后构造对象
delete pf;//将对象析构,然后释放内存

那么new的两阶段操作
(1)调用::operator new配置内存
(2)调用Foo::Foo()构造对象内容
delete算法也内含两阶段操作
(1)调用Foo::~Foo()将对象析构
(2)调用::operator delete释放内存

为了精密分工,STL allocator决定将这两阶段操作区分开来
内存配置操作由alloc::allocate()负责
内存释放操作由alloc::deallocate()负责
对象构造操作由::construct()负责
对象析构操作由::destroy()负责

构造和析构基本工具construct()和destroy()
new (p) T1(value);//在p指针指向的内存空间创建一个T1类型的对象,调用了T1的构造函数,构造函数的参数是value的类型
STL规定配置器必须拥有名为construct()和destroy()的两个成员函数,然而真正在SGI STL中大显身手的那个名为std::alloc的配置器并未遵守这一规则

SGI STL他的配置器与众不同,它的名称是alloc而非allocator,而且不接受任何参数。
一般情况下都是vector

static void* oom_malloc(size_t);
static void* oom_realloc(void*,size_t);
static void (* _malloc_alloc_oom_handler)();

注意的是
SGI没有直接使用C++的set_new_handler(),所以我们应该仿真一个类似的set_malloc_handler()
C++ new handler机制:
你可以要求系统在内存配置需求无法被满足时,调用一个你所指定的函数。
换句话说,一旦::operator new无法完成任务,在丢出std::bad_alloc异常状态之前,会先调用客端指定的处理例程。该处理例程通常即被称为new-handler。
还需注意SGI以malloc而非::operator new来配置内存
oom_malloc()和oom_realloc()后两者都有内循环,不断调用”内存不足处理例程”,期待在某次调用后获得足够的内存。
设定和设计”内存不足处理例程”是客端的责任。

第二级配置器
第二级配置器多了一些机制,避免太多小额区块造成内存的碎片。
做法
如果区块够大,超过128bytes的时候就移交第一级配置器处理。
当区块小于128bytes的时候,就用内存池管理,这又称为次层配置
次层配置
每次配置一大块内存,并维护对应之自由链表。
下次若再有想用大小的内存需求,就直接从free-list中拔出。
如果客端释放小额区块,就由配置器回收到free-list中,配置器除了负责配置也负责回收。
为了方便管理,SGI第二级配置器会主动将任何小额区块的内存需求量上调至8的倍数(30~32),并维护16个free-list,各自管理8,16,24,32,54~128字节的小额区块。
free-list的节点结构:

union obj//很重要,不会为了维护链表所必须的指针而造成内存的另一种浪费
{
     union obj* free_list_link;
     char client_data[1];//客端
}

(1)空间配置函数allocate()
判断区块大小,大于128bytes就调用第一级配置器
小于128bytes就检查对应的free list
如果free list之内有可用的区块就直接拿来用如果没有就把区块大小上调至8倍数边界,然后调用refill(),准备为free list重新填充空间。
(2)空间释放函数deallocate()
大于128调用第一级配置器
小于128找出对应free list将区块回收

重新填充free lists
对于allocate(),当它发现free list中没有可用区块了的时候就调用refill(),准备为free list重新填充空间。
新的空间取自内存池,缺省取得20个新节点(新区块),但万一内存池空间不足,获得的节点数可能小于20:

内存池
从内存池中取空间给free list使用,是chunk_alloc的工作。
stl源码剖析,书中P101

SGI容器使用配置器的方式:

template <class T,class Alloc = alloc>
class vector
{
   public:
   typedef T value_type;
   ...
   protected:
   //专属之空间配置器,每次配置一个元素大小
   typedef simple_alloc<value_type,Alloc> data_allocator;
   ...
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值