STL源码--alloc



//关于内存管理与配置,标准做法与内存池做法,内存池解决内存碎片的问题
//首先对于第一级配置器,我们直接使用new操作符就可以,第二级配置器是我们的主要战场。
//那么我们为什么设计制造第一级配置器呢?直接使用new就好了。
//首先,明确我们为什么设计两级配置器,因为我们针对不同的应用可以选择合适的配置器,那么我们就必须让配置器的使用
//对象看不出两者的差异:即,两者要有相同的接口。
//怎么样的接口?配置内存,释放内存,提供像new一样的功能。stl中有三个接口:allocate,deallocate,reallocate,
//接口设计好了,类就完成了一半。
#  if defined(__STL_NO_BAD_ALLOC) || !defined(__STL_USE_EXCEPTIONS)
#    include <stdio.h>
#    include <stdlib.h>
#    define __THROW_BAD_ALLOC fprintf(stderr, "out of memory\n"); exit(1)
#  else /* Standard conforming out-of-memory handling */
#    include <new>
#    define __THROW_BAD_ALLOC throw std::bad_alloc()
#endif
#include<cassert>
//第一级配置器:
/*
template<class _Tp>
class __malloc_alloc_template
{
public:
	static _Tp* allocate(size_t __n)
	{
		_Tp* __result=new _Tp[__n];
		return __result;
	}
	static void deallocate(_Tp* __p,size_t __n)
	{
		if(__n==1) delete __p;
		else if(__n>1) delete[] __p;
	}
};
*/
//上述设计是可以的,仅仅是包装了一下new/delete机制,仅仅换了一下他们的调用名称,但是:这个类是依赖于类型的。
//可以用,但是我们有更好的办法,可以设计出更好的类--它不依赖于具体的类型,仅仅分配字节,实现与new/delete一样的
//功能,注意这是一个静态类,因为我们不用产生不同的实体,我们仅仅是调用函数

template<int __inst>
class __malloc_alloc_template
{
	typedef void(* _Malloc_handler)();
private://首先模拟出new操作的错误处理机制--回调函数
	static void* _S_oom_malloc(size_t);
	static void* _S_oom_realloc(void*,size_t);
	static _Malloc_handler __malloc_alloc_oom_handler;
public:
	static void* allocate(size_t __n)
	{
		void* __result=malloc(__n);
		if(__result==0) _S_oom_malloc(__n);
		return __result;
	}
	//此处有点疑问
	static void deallocate(void* __p,size_t)
	{
		free(__p);
	}
	static void* reallocate(void* __p,size_t ,size_t __new_size)
	{
		void* __result=realloc(__p,__new_size);
		if(__result==0) _S_oom_realloc(__p,__new_size);
		return __result;
	}
	static void __set_malloc_handler(_Malloc_handler __f)
	{
		_Malloc_handler __old_handler=__malloc_alloc_oom_handler;
		__malloc_alloc_oom_handler=__f;
		return __old_handler;
	}
};
template<int __inst>
 __malloc_alloc_template<__inst>::_Malloc_handler 
	__malloc_alloc_template<__inst>::__malloc_alloc_oom_handler=0;

template<int __inst>
 void* __malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)
{
	_Malloc_handler  __my_oom_handler;
	void* __result;
	for(;;){
		__my_oom_handler=__malloc_alloc_oom_handler;
		if(__malloc_alloc_oom_handler==0) {__THROW_BAD_ALLOC;}
		__my_oom_handler();
		__result=malloc(__n);
		if(__result) return __result;
	}
 }
 template<int __inst>
 void* __malloc_alloc_template<__inst>::_S_oom_realloc(void* __p,size_t __n)
 {
	 _Malloc_handler __my_malloc_handler;
	 void* __result;
	 for(;;){
		 __my_malloc_handler=__malloc_alloc_oom_handler;
		 if(__malloc_alloc_oom_handler==0){__THROW_BAD_ALLOC;}
		 __my_oom_handler();
		 __result=realloc(__p,__n);
		 if(__result) return __result;
	 }
 }

 typedef __malloc_alloc_template<0>  malloc_alloc;
 //至此,第一级配置器完成,回顾一下设计目的:设计与其他配置器接口一样的配置器,功能与new./delete相似;不依赖于
 //具体类型;
 //额外的技术:回调函数机制--模拟new的异常处理



 //第二级配置器:与第一级配置器的接口相同,不过,内存碎片少,涉及了新的思想,
 //我们设计一个存储着内存的链表,程序使用的内存,我们直接从链表中提取,程序释放的内存存储为链表的一个节点,这样我们
 //就让链表扮演者“heap”的角色,但是还是那句话,构建依赖于类型的链表好构建,因为节点的大小已经确定,但是
 //我们的目的设设计通用的1,不依赖于类型的配置器,所以,链表不依赖与类型,而依赖于内存大小;若果不依赖于类型
 //我们怎么知道需要内存大小?
 //:这里的设计策略是,我们维护16个链表,每个链表存储着8*n byte大小的节点,最大的节点是128byte.这样,内存需要7byte
 //我们就分配给它8byte,因为小于128byte的内存都是从链表中得到的,所以我们回收的时候,如果回收对象7byte,那么就回收
 //8byte,注意,要实现这一功能,就得好好利用指针。
 //如果链表中的内存不够用了怎么办?
 //:我们从内存池里填充,这里的内存池是由两个指针标示的一段区域
 //如果内存池内存不够怎么办?
 //:从heap里分配
 //如果heap内存不够怎么办?抛出异常
 template<int __inst>
 class __default_alloc_template
 {
 private:
	 enum{__ALIGN=8};//链表的节点大小都是此值的倍数
	 enum{__MAX_BYTE=128};//链表中结点的最大大小
	 enum{__NFREELISTS=16};//维持的链表条数
	 //不知道为什么,作者这样实现这几个值,而且不是用一个enum;
	 static size_t _S_round_up(size_t __n)//将__n调整为大于它的8的倍数,即:可以在链表中找到相应大小的节点
	 {
		 return (__n+__ALIGN-1)&~(__ALIGN-1);
	 }
	 
	 union _Obj
	 {//链表中节点的结构
		 union _Obj* _M_free_list_link;
		 char data[1];
	 };
	 static _Obj* _S_free_list[_NFREELISTS];//维持着16条链表,每条链表中的节点内存都不一样,同一链表中结点内存一样

	 static size_t _S_free_index(size_t __n)//对于__n寻找合适的链表索引
	 {
		 return (((__n+__ALIGN)&~(__ALIGN-1))/__ALIGN)-1;
	 }
	 //下面两个函数的功能解决的问题:当节点大小为__n的链表为空时,我们需要填充链表。
	 //从内存池中分配内存,填充这条链表;如果内存池为空,则从heap中分配内存填充内存池
	 //我们很自然的分为两层,第一层:用内存池中的内存填充链表;第二层:用heap中的内存填充内存池
	 //可是,这样分层,却不是很好操作,因为第一层有两个动作:从内存池种分配内存;填充链表;两个动作都比较复杂
	 //第二层的两个动作都比较简单
	 //于是,函数操作分三层:从heap中的内存填充内存池;从内存池分配内存;填充链表
	 static void* _S_refill(size_t __n);
	 static void* _S_chunk_alloc(size_t __n,int& __nobjs);//引用传递,我们可以更改
	 static void* _S_malloc(size_t __n);
	 
	 //数据成员
	 static char* _S_start_free;//内存池的起始位置
	 static char* _S_end_free;//结束位置
	 static size_t _S_heap_size;//没弄明白什么作用


 public:
	 //配置器的统一接口
	 static void* allocate(size_t __n)
	 {
		 void* __result;
		 if(__n>__MAX_BYTE){
			__result= malloc_alloc::allocate(__n);
		 }else{
			 _Obj** __my_free_list=_S_free_list+_S_free_index[__n];
			 if(*__my_free_list==0)
				__result= _S_refill(_S_round_up(__n));
			 else{
				 __result=*__my_free_list;
				 (*__my_free_list)=(*__my_free_list)->_M_free_list_link;
			 }
		 }
		 return __result;
	 }

	 static void deallocate(void* __p,size_t __n)
	 {
		 if(__n>__MAX_BYTE){
			 malloc_alloc::deallocate(__p,__n);
		 }else{
			 _Obj** __my_free_list=_S_free_list+_S_free_index(__n);
			 ((_Obj*)__p)->_M_free_list_link=*__my_free_list;
			 *__my_free_list=(_Obj*)__p;
		 }
	 } 
	 static void* reallocate(void* __p,size_t __old_size,size_t __new_size);
 };
 /*我的实作版本,所用的是倒序添加,即每添加一个元素都是添加到链表的开头
 template<int __inst>
 void* __default_alloc_template<__inst>::_S_refill(size_t __n)//__n是8的倍数
 {
	 int __nobjs=20;
	 void* __chunk=_S_chunk_alloc(__n,__nobjs);
	 if(__nobjs==1) return __chunk;
	 _Obj** __my_free_list=_s_free_list+_S_free_index(__n);
	 _Obj* __next_obj=(_Obj*)((char*)__chunk+__n);
	 __next_obj->_M_free_list_link=0;
	 _Obj* __current_obj;
	 *__my_free_list=__next_obj;
	 for(int __i=2;__i<__nobjs;++__i){
		 __current_obj=__next_obj;
		 __next_obj=_Obj*((char*)__next_obj+__n);
		 __next_obj->_M_free_list_link=*__my_free_list;
		 *__my_free_list=__next_obj;
	 }
	 return __chunk;
 }
 */
 //stl的实作版本,正序添加即:添加的每个元素都添加到元素的结尾,这里依赖的事实是:当重新填充链表时,链表已经为空
 template<int __inst>
 void* __default_alloc_template<__inst>::_S_refill(size_t __n)//__n是8的倍数
 {
	 int __nobjs = 20;
    char* __chunk = _S_chunk_alloc(__n, __nobjs);
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;

    if (1 == __nobjs) return(__chunk);
    __my_free_list = _S_free_list + _S_freelist_index(__n);

    /* Build free list in chunk */
      __result = (_Obj*)__chunk;
      *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
      for (__i = 1; ; __i++) {//注意此处的条件判断的转移
        __current_obj = __next_obj;
        __next_obj = (_Obj*)((char*)__next_obj + __n);
        if (__nobjs - 1 == __i) {
            __current_obj -> _M_free_list_link = 0;
            break;
        } else {
            __current_obj -> _M_free_list_link = __next_obj;
        }
      }
	  
    return(__result);
	//我对上段程序不敢苟同,我没有看出任何的c++风格,并且我觉得它的效率很低
	 /* //我的实作版本
	 int __nobjs=20;
	 void* __chunk=_S_chunk_alloc(__n,__nobjs);
	 if(__nobjs==1) return __chunk;
	 _Obj** __my_free_list=_s_free_list+_S_free_index(__n);
	 _Obj* __next_obj=(_Obj*)((char*)__chunk+__n);
	 _Obj* __current_obj;
	 *__my_free_list=__next_obj;
	 for(int __i=2;__i<__nobjs;++__i){
		 __current_obj=__next_obj;
		 __next_obj=_Obj*((char*)__next_obj+__n);
		 __current_obj->_M_free_list_link=__next_obj;
	 }
	 __next_obj->_M_free_list_link=0;
	 return __chunk;
	 */
}

//----------------------------------------------------------------------
 //从内存池中分配大小为__size的__nobjs个对象
 template<int __inst>
 void* __default_alloc_template<__inst>::_S_chunk_alloc(size_t __size,int& __nobjs)
 {
	 void* __result;
	 size_t __byte_left=_S_end_free-_S_start_free;
	 size_t __total_byte=__size*__nobjs;
	 if(__byte_left>__total_byte){//内存池中的内存足够
		 __result=_S_start_free;
		 _S_start_free+=__total_byte;
		 return __result;
	 }else if(__byte_left>=__size){//内存池中内存不足,但是足够一个__size
		 __nobjs=__byte_left/__size;
		 __total_byte=__nobjs*__size;
		 __result=_S_start_free;
		 _S_start_free+=__total_byte;
		 return __result;
	 }else{//连一个__size都不够
		 size_t __byte_to_get=2*__total_byte+ _S_round_up(_S_heap_size >> 4);//不知道为什么
		 //先处理到内存池中剩余的byte
		 if(__byte_left>0){
			 //这里有疑问,如果__byte_left=14,那么_S_free_index(__byte_left)返回1,那么对应的节点大小为16byte,而
			 //剩余内存不足以支撑起来,所以我认为还得减一,除非我们分配的都是8的倍数,剩余的都是8的倍数
			 _Obj** __my_free_list=_S_free_list+_S_free_index(__byte_left);
			 ((_Obj*) _S_start_free)->_M_free_list_link=*__my_free_list;
			 *_my_free_list=_S_start_free;
		 }

		 _S_start_free=(char*)malloc(__byte_to_get);
		 if(_S_start_free==0){//内存分配失败
			 //这时候就是把链表中大于__size的节点分配给内存池
			 _Obj** __my_free_list;
			 for(size_t __i=__size;__i<__MAX_BYTE;__i+=__ALIGN){
				 __my_free_list=_S_free_list+_S_free_index(__size);
				 __p=*__my_free_list;
				 if(__p!=0){
					 *__my_free_list=__p->_M_free_list_link;
					 _S_start_free=(char*)__p;
					 _S_end_free=(char*)__p+__i;
					 return (_S_chunk_alloc(__size,__nobjs));
				 }
			 }
			 _S_end_free=0;
			 _S_start_free=(char*)malloc_alloc::allocate(__byte_to_get);//使用异常处理机制
		 }
		 _S_heap_size+=__byte_to_get;
		 _S_end_free=_S_start_free+_byte_to_get;
		 return _S_chunk_alloc(__size,__nobjs);
	 }
 }

 template<int __inst>
 void* __default_alloc_template<__inst>::reallocate(void* __p,size_t __old_size,size_t __new_size)
 {
	 if(__old_size>___MAX_BYTE && __new_size>___MAX_BYTE){
		 malloc_alloc::reallocate(__p,__old_size,__new_size);
	 }else if(_S_round_up(__old_size)==_S_round_up(__new_size)){
		 return __p;
	 }else{
		 void* __result=allocate(__new_size);
		 size_t __copy_sz=__new_size>__old_size?__old_size:__new_size;
		 memcpy(__result,__p,__copy_sz);
		 deallocate(__p,__old_size);
		 return __result;
	 }
 }

 template<int __inst> char* __default_alloc_template<__inst>::_S_start_free=0;
 template<int __inst> char* __default_alloc_template<__inst>::_S_end_free=0;
 template<int __inst> size_t __default_alloc_template<__inst>::_S_heap_size=0;
 template<int __inst> typename __default_alloc_template<__inst>::_Obj*
	 __default_alloc_template<__inst>::_S_free_list[
		  __default_alloc_template<__threads, __inst>::_NFREELISTS ]=
		  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

 typedef __default_alloc_template<0> alloc;
 //第二级配置器的主要是内存与指针的控制,内存池技术
 
 //下面的一个类的主要目的是介绍指针的技术:我们在内存的开始处,记录这块内存的大小
 template<class _Alloc>
 class debug_alloc
 {
 private:
	 enum {_S_extra=8};//8byte足够存储任意大的整数
 public:
	 static void* allocate(size_t __n)
	 {
		 char* __result= (char*) _Alloc::allocate(__n);
		 *(size_t*)__result=__n;
		 return __result+(int)_S_extra;
	 }
	 static void deallocate(void* __p,size_t __n)
	 {
		 char* __real_p=(char*)__p-_S_extra;
		 assert(*(size_t*)__real_p==__n);
		 _Alloc::deallocate(__real_p,__n+_S_extra);
	 }
	 static void* reallocate(void* __p,size_t __old_sz,size_t __new_sz)
	 {
		 char* __real_p=(char*)__p-_S_extra;
		 assert(*(size_t*)__real_p==__old_sz);
		 char* __result=(char*)_Alloc::reallocate(__p,__old_sz_+_S_extra,__new_sz+_S_extra);
		 *(size_t*)__result=__new_sz;
		 return __result+_S_extra;
	 }
 };

 //要知道,我们使用时分配内存是需要依赖于类型的,所以,我们需要一层封装,可以不用直接跟内存大小打交道,而是和
 //对象的个数打交道。
 template<class _Tp,class _Alloc>
 class simple_alloc
 {
 public:
	 static void* allocate(size_t __n){return _Alloc::allocate(__n*sizeof(_Tp));}
	 static void* allocate(void){return _Alloc::allocate(sizeof(_Tp));}
	 static void deallocate(void* __p,size_t __n){if(__n!=0) _Alloc::deallocate(__p,__n*sizeof(_Tp));}
	 static void deallocate(void* __p){ _Alloc::deallocate(__p,sizeof(_Tp));}
 };

 //我们的四个allocator
 typedef __default_alloc_template<0> alloc;
 typedef __default_alloc_template<0> single_client_alloc;

 //还有malloc_alloc,pthread_alloc

//c++ 标准allocator定义
 template<class _Tp>
 class allocator
 {
	 typedef alloc _Alloc;
 public:
  typedef size_t     size_type;
  typedef ptrdiff_t  difference_type;
  typedef _Tp*       pointer;
  typedef const _Tp* const_pointer;
  typedef _Tp&       reference;
  typedef const _Tp& const_reference;
  typedef _Tp        value_type;

  template<class _Tp1>
  struct rebind
  {
	  typedef allocator<_Tp1> other;
  };

  allocator() {}
  allocator(const allocator&)  {}
  template <class _Tp1> allocator(const allocator<_Tp1>&) __STL_NOTHROW {}
  ~allocator() {}

  pointer address(reference __x){return &__x;}
  const_pointer address(const_reference __x) const { return &__x; }
    // __n is permitted to be 0.  The C++ standard says nothing about what
  // the return value is when __n == 0.
  _Tp* allocate(size_type __n, const void* = 0) {
    return __n != 0 ? static_cast<_Tp*>(_Alloc::allocate(__n * sizeof(_Tp))) 
                    : 0;
  }

  // __p is not permitted to be a null pointer.
  void deallocate(pointer __p, size_type __n)
    { _Alloc::deallocate(__p, __n * sizeof(_Tp)); }

  size_type max_size() const  
    { return size_t(-1) / sizeof(_Tp); }//max:size_t(-1)

  void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); }
  void destroy(pointer __p) { __p->~_Tp(); }
};
//void 的特化版本
 template<>
class allocator<void> {
public:
  typedef size_t      size_type;
  typedef ptrdiff_t   difference_type;
  typedef void*       pointer;
  typedef const void* const_pointer;
  typedef void        value_type;

  template <class _Tp1> struct rebind {
    typedef allocator<_Tp1> other;
  };
};
//上面最不容易理解的可能就是rebind的作用了,其实他的作用也不是很大,
//根据msdn的例子http://msdn.microsoft.com/en-us/library/5fk3e8ek.aspx,我们可知道他的作用,主要是用于容器

//下面是alloc_traits,主要是两个成员,一是真正的allocator,二是是否需要allocator实例对象。一般来说,allocator是不需要实例的,
//因为allocator多事静态函数,但是,stl文档中是这样描述的:
/* This is occasionally a significant restriction. For example, it is not possible to arrange for different containers to 
   allocate memory mapped to different files by passing different allocator instances to the container constructors. 
   Instead one must use one of the following alternatives:
   1.The container classes must be instantiated with different allocators, one for each file. This results in different 
   container types. This forces containers that may be mapped to different files to have distinct type, which may be a
   troublesome restriction, though it also results in compile-time detection of errors that might otherwise be difficult 
   to diagnose.
   2.The containers can be instantiated with a single allocator, which can be redirected to different files by calling
   additional member functions. The allocator must be suitably redirected before container calls that may allocate. 
 */
//对于alloc_traits,没理解好暂时不写。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值