SGI特殊空间配置器std::alloc

alloc不接受任何template类型参数,它只是一个别名。

另外,new对象操作实际执行两个过程:

1>调用::operator new 配置内存。

2>调用相应的构造函数,构造对象。

delete对象也执行两个过程:

1> 调用相应的析构函数,析构对象。

2> 调用::operator delete 释放内存。

为了精密分工,STL allocator将 两个过程分开处理,配置内存alloc::allocate()负责,释放内存alloc::deallocate()负责;该过程定义在文件stl_alloc.h中;构造对象::construct()负责,析构对象::destory()负责,该过程定义在文件stl_construct.h中。


1 构造和析构基本工具

construct()与destory()关键在于析构工具destory(),因为该工具用到了traits技巧_type_traits,除了_type_traits,还有iterator_traits,iterator_traits负责提取迭代器的特性,_type_traits负责提起类别的特性。这两种traits贯穿整个STL,因此这里着重介绍_type_traits。


1.1 _type_traits

  _type_traits定义在文件<type_traits.h>中
首先定义了两种类型 _true_type 和 _false_type

struct __true_type {
};

struct __false_type {
};

然后,定义_type_traits的默认版本,默认类型都是_false_type

template <class _Tp>
struct __type_traits { 
   typedef __false_type    has_trivial_default_constructor;
   typedef __false_type    has_trivial_copy_constructor;
   typedef __false_type    has_trivial_assignment_operator;
   typedef __false_type    has_trivial_destructor;//trivial 是无用的意思
   typedef __false_type    is_POD_type;//POD plain old data 指与C兼容的类型
};


然后定义 _type_traits的特化版本:_Tp为特定类型时的版本。

STL分别特化了_Tp为:bool,char, signed char ,unsigned char,wchar_t,short,unsigned short,int,unsigned int,long,unsigned long,long long,unsigned, long long,float,double,long double,char*,signed char*,unsigned char*,const char*,const signed char*,const unsigned char*的版本。这些版本的定义是相同的,都是将 _false_type 改为了 _true_type。说明这些类型都是POD类型的。以char类型为例,定义如下:

struct __type_traits<char> 
{
	//这里全部是_true_type类型
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};


1.2析构工具destory()

destory()有两个版本,第一个是接收一个指针类型参数的:这个没什么奥秘
template<typename _Tp>
inline void _Destroy(_Tp* __pointer)
    { __pointer->~_Tp(); }
另外一个版本是接收两个迭代器类型参数的,这个版本的奥秘在于用到了iteraotr_traits和_type_traits。

 template<class ForwardIterator>
 inline void _destory(ForwardIterator first,ForwardIterator last)
 {
	 //这里value_type实际是用到了iterator_traits
	 //以后会详细介绍
	 _destory(first,last,value_type(first));
 }
 template<class ForwardIterator,class T>
 inline void _destory(ForwardIterator first,ForwardIterator ,last,T*)
 {
	 //这里用到了_type_traits,根据T的类型,has_trivial_destructor为_true_type_或_false_type_类型
	 typedef typename _type_traits<T>::has_trivial_destructor trivial_destructor;
     _destory_aux(first,last,trivial_destructor());	 
 }
 //aux 是辅助的意思
 //_destory_aux根据第三个参数的不同类型,有两个版本
 template<class ForwardIterator>
 inline void _destory_aux(ForwardIterator first,ForwardIterator last,_false_type)
 {
	 //析构函数不是 trivial类型,即对象不是POD类型的
	 //调用destory()函数,间接调用了对象的析构函数
	 for(;first<last;++first;)
		 destroy(&*first);
 }
 template<class ForwardIterator>
 inline void _destory_aux(ForwardIterator first,ForwardIterator last,_true_type)
 {
	 //析构函数是 trivial类型,即对象是POD类型的
	 //什么都不做,对象自动销毁。这样不用一次次调用析构函数,效率提高
 }




2 空间的配置与释放


C++的内存配置基本操作是::operator new()与::operator delete()。这两个全局函数类似于C语言的malloc()与free()。STL用的是malloc()与free()完成内存的配置与释放。因为C++并没有提供类似C语言中realloc()的函数。
空间的配置与释放定义在文件<stl_alloc.h>中,主要需要考虑的问题是:
1> 应对内存不足的策略。
2> 解决小区域块内存引起的“内存碎片”问题

为了解决“内存碎片问题”,STL采取的措施是 双层配置器。至于只用一级配置器,还是两个配置器都用 取决于是否定义_USE_MALLOC。二级配置器的工作流程图如下



另外一个需要注意的是,配置单位的问题,malloc配置单位是byte,可将配置单位改为个别元素的大小(sizeof(T))
template<class _Tp, class _Alloc>
class simple_alloc 
{

public:
    //重新对_Alloc::allocate进行封装
	//使配置单位从byte变成了个别元素的大小(sizeof(_Tp))
    static _Tp* allocate(size_t __n)
      { return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp)); }
    static _Tp* allocate(void)
      { return (_Tp*) _Alloc::allocate(sizeof (_Tp)); }
    static void deallocate(_Tp* __p, size_t __n)
      { if (0 != __n) _Alloc::deallocate(__p, __n * sizeof (_Tp)); }
    static void deallocate(_Tp* __p)
      { _Alloc::deallocate(__p, sizeof (_Tp)); }
};


1. 一级配置器
一级配置器直接使用malloc()与free()管理内存。使用malloc()而不是::operator new(),的原因是,C++并没有提供类似C语言中realloc()的函数。
C++中有个C++ new _handler机制,可以要求系统在内存配置无法满足时调用一个你指定的函数。因为一级配置器中没有用到::operator new()所以,不能用C++ 的set_new_handler()。解决方法是,仿造一个功能类似的set_malloc_handler()。

先了解一下C++ new_handler机制

namespace std
{
	//new_handler 是一个typdef表示void()类型的函数指针
	typedef void (* new_handler)();
	//set_new_handler 输入和输出都是一个new_handler
	//输入的new_handler p表示new失败后要执行的参数
	new_handler set_new_handler(new_handler p)throw();
}
函数指针typedef的用法

typedef int (*MYFUN)(int, int)
MYFUN是个函数指针,该函数类型为 输入两个int,输出一个int


2.二级配置器

当区域块小于128bytes时,则以内存池(memory pool)管理。
原理是:
用16个free-list分别管理大小分别为8、16、24、32、40、48、56、64、72、80、88、96、104、112、120、128bytes的小区域块。如果小区域块大小不为上边的16个大小,则上调至8的倍数,例如:25,则会上调至32,方法是:(n+7)& ~(7);
每次分配一大块内存,由free-list维护,如果下次有相同大小的内存需求时,直接从free-list中取出。





解释上图:
刚开始客户调用chunk_alloc(32,20) ,于是通过malloc配置40个32bytes的区域,第一个交出,另19个交给free_list[3]维护,剩余的20个给内存池;然后客户调用chunk_alloc(64,20),此时free-list[7]为空,必须向内存池要内存,但是内存池只能提供(20*32)、64=10个64bytes的区块,就将这10个区块返回,一个给客户,剩余9个由free_list[7]维护。此时内存池为空,然后客户调用chunk_alloc(96,20),此时free_list[11]也为空,内存池也为空,于是用malloc()配置40+n(附加量)个96bytes的区块,第一个给客户,另19个交个free_list[11]维护,剩余的20+n个96bytes的区块留给内存池。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值