|  | 
				
					
	
		
		
		
		   使用STL已经有一段时间了,对里面的运作方式一直不了解。这几天突发其想,找了一些关于STL源码的书看了一下,觉得其内部实现非常精妙。作为进一步学习,我打算把STL中的主要组件自己动手实现一下。首先从空间配置器开始,从内部逐渐了解STL中各种容器的实现细节。
 根据STL的规范,allocator必须有以下接口:
 
 
				 typedef    size_t        size_type; 
  typedef    T        value_type; 
  typedef    T
				*
				        pointer; 
  typedef    ptrdiff_t    difference_type; 
  typedef    
				const
				 T
				*
				    const_pointer; 
  typedef    T
				&
				        reference; 
  typedef    
				const
				 T
				&
				    const_reference; 
  
  rebind 
  allocator(
				const
				 allocator
				<
				U
				>&
				 ) 
  ~
				allocator() 
  pointer address(reference x) 
  const_pointer address(const_pointer x) 
  T
				*
				 allocate(size_type n, 
				void
				*
				 p 
				=
				 
				0
				) 
  void
				 deallocate(pointer p, size_type n) 
  size_type max_size() 
  void
				 construct(pointer p, const_reference val) 
  void
				 destroy(pointer p)  SGI STL并没有按C++ STD的说没直接做一个Allocator出来,而是先做出两个Allocator template及以内存池的形式来构 //造Allocator比STD::allocator效率更高且减少了内存碎片。下面是sgi_allocator.h的代码:
 
				 #ifndef SGI_ALLOCATOR 
  #define
				 SGI_ALLOCATOR 
  #include 
				<
				iostream
				> 
  #include 
				<
				cstddef
				>
				        
				//
				for size_t 
  using
				 
				namespace
				 std; 
  
  namespace
				 SGI 
     { 
  template
						<
						int
						 inst
						> 
  class
						 malloc_alloc_template    
						//
						一级空间配置器 
      { 
  public
								: 
  static
								 
								void
								*
								 allocate(size_t n) 
     { 
  void
										*
										 result 
										=
										 malloc(n); 
  if
										 (result 
										==
										 NULL) 
     { 
  result 
												=
												 oom_malloc(n);        
												//
												内存不足调用oom_malloc()进行处理 
  } 
   
  return
										 result; 
  } 
   
  static
								 
								void
								 deallocate(
								void
								*
								 p, size_t)    
								//
								第二个参数无所谓,只要一进来就把它干掉.. 
      { 
  free(p); 
  } 
   
    static
								 
								void
								*
								 reallocate(
								void
								*
								 p, size_t
								/**/
								
										/*
										old size
										*/
								
								, size_t new_size) 
     { 
  void
										*
										 result 
										=
										 realloc(p, new_size); 
  if
										(result 
										==
										 NULL) 
     { 
  result 
												=
												 oom_realloc(p, new_size); 
  } 
   
  return
										 result; 
  } 
   
  static
								 
								void
								 (
								*
								set_malloc_handler(
								void
								 (
								*
								f)()))() 
     { 
  void
										 (
										*
										old)() 
										=
										 oom_malloc_handler; 
  oom_malloc_handler 
										=
										 f; 
  
  return
										 old; 
  } 
  private
								: 
  static
								 
								void
								*
								 oom_malloc(size_t);        
								//
								内存不足时调用这个函数(因为里面有处理函数) 
  static
								 
								void
								*
								 oom_realloc(
								void
								*
								, size_t);
								//
								同上 
  static
								 
								void
								 (
								*
								oom_malloc_handler)();    
								//
								内存不足时的处理函数 
  }
						
						; 
  template
						<
						int
						 inst
						> 
  void
						*
						 malloc_alloc_template
						<
						inst
						>
						::oom_malloc(size_t size) 
     { 
  void
								*
								 result; 
   
  while
								(
								true
								)            
								//
								反得调用处理函数,直到申请成功  .如果没有定义处理函数则抛出异常 
      { 
  if
										 (oom_malloc_handler 
										==
										 NULL) 
     { 
  throw
												 bad_alloc(); 
  } 
  (
										*
										oom_malloc_handler)(); 
  if
										 ( (result 
										=
										 malloc(size)) 
										!=
										 NULL) 
     { 
  return
												 result; 
  } 
  } 
  } 
   
  template
						<
						int
						 inst
						> 
  void
						*
						 malloc_alloc_template
						<
						inst
						>
						::oom_realloc(
						void
						*
						 p, size_t new_size) 
     { 
  void
								*
								 result; 
  
  while
								(
								true
								) 
     { 
  if
										 (oom_malloc_handler 
										==
										 NULl) 
     { 
  throw
												 bad_alloc(); 
  } 
  (
										*
										oom_alloc_handler)(); 
  if
										 ( (result 
										=
										 realloc(p, new_size)) 
										!=
										 NULL) 
     { 
  return
												 result; 
  } 
  } 
  } 
   
  template
						<
						int
						 inst
						> 
  void
						 (
						*
						malloc_alloc_template
						<
						inst
						>
						::oom_malloc_handler)() 
						=
						 NULL; 
  
  typedef malloc_alloc_template
						<
						0
						>
						 malloc_alloc;        
						//
						到此完成一级空间配置器的定义 
   
    /**/
						
								////////////////////////////////////////////////////// 
  //
						下面是对一二级配置器的封装, 其中Alloc即为空间配置器 
  //
						默认用的是二级配置器,当>128K或内存不足时会交给一级 
  //
						配置器处理 
    /**/
						
								///////////////////////////////////////////////////
								//j 
  template
						<
						typename T, typename Alloc
						> 
  class
						 simple_alloc 
     { 
  public
								: 
  static
								 T
								*
								 allocate(size_t n) 
     { 
  return
										 n 
										==
										 
										0
										 
										?
										 
										0
										 : (T
										*
										)Alloc::allocate(n 
										*
										 
										sizeof
										(T)); 
  } 
   
  static
								 T
								*
								 allocate() 
     { 
  return
										 (T
										*
										)Alloc::allocate(
										sizeof
										(T)); 
  } 
   
  static
								 
								void
								 deallocate(T
								*
								 p, size_t n) 
     { 
  if
										 (n 
										!=
										 
										0
										) 
     { 
  Alloc::deallocate(p, n 
												*
												 
												sizeof
												(T));        
												//
												这里要用两个参数是为了与后面的二级配置器配合(这个问题郁闷了我好久,呵呵,菜啊!) 
  } 
  } 
   
  static
								 
								void
								 deallocate(T
								*
								 p) 
     { 
  Alloc::deallocate(p, 
										sizeof
										(T)); 
  } 
  }
						
						; 
  
   enum  {ALIGN 
								=
								 
								8
								, MAX_BYTES 
								=
								 
								128
								, NFREELISTS 
								=
								 
								16
								}
						
						;    
						//
						 其中NFREELISTS = MAX_BYTES / ALIGN 
   
  template
						<
						bool
						 threads, 
						int
						 inst
						> 
  class
						 default_alloc_template    
						//
						定义二级配置器 
      { 
  public
								: 
  static
								 
								void
								*
								 allocate(size_t n) 
     { 
  void
										*
										 result; 
   
  if
										 (n 
										>
										 (size_t)MAX_BYTES) 
     { 
  result 
												=
												 malloc_alloc::allocate(n);        
												//
												>128K交给一级配置器处理 
  } 
  else 
      { 
  Obj
												*
												 
												volatile
												*
												 my_free_list    
												=
												 free_list 
												+
												 free_list_index(n); 
  if
												 ( (result 
												=
												 
												*
												my_free_list) 
												==
												 NULL) 
     { 
  result    
														=
														    refill(round_up(n)); 
  } 
  else 
      { 
  Obj
														*
														 tmp        
														=
														 
														*
														my_free_list; 
  *
														my_free_list    
														=
														 tmp
														->
														free_list_link; 
  result            
														=
														 tmp; 
  } 
  } 
   
  return
										 result; 
  } 
   
  static
								 
								void
								 deallocate(
								void
								*
								 p, size_t n) 
     { 
  if
										 (n 
										>
										 (size_t)MAX_BYTES) 
     { 
  malloc_alloc::deallocate(p, n); 
  } 
  else 
      { 
  Obj
												*
												 
												volatile
												*
												 my_free_list 
												=
												 free_list 
												+
												 free_list_index(n); 
  Obj
												*
												 q 
												=
												 (Obj
												*
												) p; 
  q
												->
												free_list_link    
												=
												 
												*
												my_free_list; 
  *
												my_free_list        
												=
												 q; 
  } 
  } 
   
  static
								 
								void
								*
								 reallocate(
								void
								*
								 p, size_t old_size, size_t new_ize); 
  
  private
								: 
  static
								 size_t round_up(size_t bytes) 
     { 
  return
										 ((bytes 
										+
										 (size_t)ALIGN 
										-
										 
										1
										) 
										&
										 
										~
										((size_t)ALIGN 
										-
										 
										1
										)); 
  } 
   
  union Obj            
								//
								Trick,既可以作指针,又可以作为内存地址   
      { 
  union Obj
										*
										 free_list_link; 
  char
										 client_data[
										1
										]; 
  }
								
								; 
  
  static
								 Obj
								*
								 
								volatile
								 free_list[NFREELISTS]; 
  
  static
								 size_t free_list_index(size_t size) 
     { 
  return
										 ((size 
										+
										 (size_t)ALIGN 
										-
										 
										1
										) 
										/
										 (size_t)ALIGN 
										-
										1
										); 
  } 
   
  static
								 
								void
								*
								 refill(size_t n); 
  static
								 
								char
								*
								 chunk_alloc(size_t size, size_t
								&
								 objs); 
   
  static
								 
								char
								*
								    chunk_start;    
								//
								内存池起始位置 
  static
								 
								char
								*
								    chunk_end;        
								//
								内存池结束位置 
  static
								 size_t    heap_size;        
								//
								从开始至今在堆中申请的字节总数 
   
  }
						
						; 
  
  template
						<
						bool
						 threads, 
						int
						 inst
						> 
  char
						*
						 default_alloc_template
						<
						threads, inst
						>
						::chunk_start 
						=
						 NULL; 
  
  template
						<
						bool
						 threads, 
						int
						 inst
						> 
  char
						*
						 default_alloc_template
						<
						threads, inst
						>
						::chunk_end 
						=
						 NULL; 
  
  template
						<
						bool
						 threads, 
						int
						 inst
						> 
  size_t default_alloc_template
						<
						threads, inst
						>
						::heap_size 
						=
						 
						0
						; 
   
  template
						<
						bool
						 threads, 
						int
						 inst
						> 
    typename default_alloc_template
						<
						threads, inst
						>
						::Obj
						*
						 
						volatile
						 default_alloc_template
						<
						threads, inst
						>
						::free_list[NFREELISTS] 
						=  {
								0
								,
								0
								,
								0
								,
								0
								,
								0
								,
								0
								,
								0
								,
								0
								,
								0
								,
								0
								,
								0
								,
								0
								,
								0
								,
								0
								,
								0
								,
								0
								}
						
						; 
  
  
  //
						核心部分,从内存池中申请空间 
  template
						<
						bool
						 threads, 
						int
						 inst
						> 
  char
						*
						 default_alloc_template
						<
						threads, inst
						>
						::chunk_alloc(size_t size, size_t
						&
						 nobjs) 
     { 
  char
								*
								    result        
								=
								 NULL; 
  size_t    total_bytes    
								=
								 size 
								*
								 nobjs; 
  size_t    bytes_left    
								=
								 chunk_end 
								-
								 chunk_start;            
								//
								内存池所剩水量 
   
  if
								 ( bytes_left 
								>
								 total_bytes)                            
								//
								内存池中有足够空间可以分配 
      { 
  result        
										=
										    chunk_start; 
  chunk_start 
										+=
										    total_bytes; 
  
  return
										 result; 
  } 
  else
								 
								if
								(bytes_left 
								>=
								 size)                        
								//
								内存池中空间至少可以分配一个块 
      { 
  nobjs            
										=
										    (
										int
										)bytes_left 
										/
										 size; 
  total_bytes        
										=
										    size 
										*
										 nobjs; 
  result            
										=
										    chunk_start; 
  chunk_start    
										+=
										    total_bytes; 
  
  return
										 result; 
  } 
  else
								                                        
								//
								内存池已山穷水尽  一个块都无法分配出来 T_T 
      { 
  size_t bytes_to_get    
										=
										 total_bytes 
										+
										 round_up(heap_size 
										>>
										 
										4
										);        
										//
										申请总数两位加上扩展 
   
  if
										 (bytes_left 
										>
										 
										0
										) 
     { 
  Obj
												*
												 
												volatile
												*
												 my_free_list            
												=
												 free_list 
												+
												 free_list_index(bytes_left); 
  ((Obj
												*
												)chunk_start)
												->
												free_list_link    
												=
												 
												*
												my_free_list; 
  *
												my_free_list                        
												=
												 (Obj
												*
												)chunk_start; 
  } 
  chunk_start    
										=
										    (
										char
										*
										)malloc(bytes_to_get); 
  if
										 (chunk_start 
										==
										 NULL)        
										//
										内存不足  这下麻烦了,看看表中有没有空间可以回收   
      { 
  Obj
												*
												 
												volatile
												*
												 my_free_list; 
  Obj
												*
												 p; 
  
  for
												(size_t i 
												=
												 size; i 
												<=
												 MAX_BYTES; i 
												+=
												 ALIGN) 
     { 
  my_free_list    
														=
														    free_list 
														+
														 free_list_index(i); 
  p                
														=
														    
														*
														my_free_list; 
  
  if
														 (p 
														!=
														 NULL)    
														//
														找到适合的空间,进行回收 
      { 
  *
																my_free_list    
																=
																    p
																->
																free_list_link; 
  chunk_start        
																=
																    (
																char
																*
																)p; 
  chunk_end        
																+=
																    i; 
  
  return
																 chunk_alloc(size, nobjs); 
  
  } 
  } 
   
  //
												完全走投无路了,只好看看内存不足的处理函数能不能帮上忙 
  chunk_end    
												=
												    NULL; 
  chunk_start    
												=
												    (
												char
												*
												)malloc_alloc::allocate(bytes_to_get); 
  } 
  chunk_end    
										+=
										    bytes_to_get; 
  heap_size    
										+=
										    bytes_to_get; 
  
  return
										 chunk_alloc(size, nobjs); 
  } 
  } 
   
    /**/
						
								/* 
  * 返回一个大小为n的块, 并且可能适当地为free_list增加节点 
  * 
  */ 
  template
						<
						bool
						 threads, 
						int
						 inst
						> 
  void
						*
						 default_alloc_template
						<
						threads, inst
						>
						::refill(size_t n) 
     { 
  size_t        nobjs 
								=
								 
								20
								;                
								//
								默认申请块的个数 
  Obj
								*
								        curr; 
  Obj
								*
								        next; 
   
  
  char
								*
								    result 
								=
								 chunk_alloc(n, nobjs); 
  if
								 (nobjs 
								==
								 
								1
								)        
								//
								只申请到一块空间则直接返回给调用者 
      { 
  return
										 result; 
  } 
  else
								            
								//
								申请到多于一个块,将其它nobjs - 1个块加到free_list中 : ) 
      { 
  Obj
										*
										 
										volatile
										*
										 my_free_list    
										=
										 free_list 
										+
										 free_list_index(n);; 
  *
										my_free_list 
										=
										 next 
										=
										 (Obj
										*
										)(result 
										+
										 n); 
  for
										(
										int
										 i 
										=
										 
										1
										; ; 
										++
										i) 
     { 
  curr    
												=
												    next; 
  next    
												=
												 (Obj
												*
												)( (
												char
												*
												)next 
												+
												 n); 
  
  if
												 (i 
												==
												 nobjs 
												-
												 
												1
												) 
     { 
  curr
														->
														free_list_link    
														=
														    NULL; 
  break
														; 
  } 
  else 
      { 
  curr
														->
														free_list_link    
														=
														    next; 
  } 
  } 
  return
										 result; 
  } 
  } 
   
  template
						<
						bool
						 threads, 
						int
						 inst
						> 
  static
						 
						void
						*
						 default_alloc_template
						<
						threads, inst
						>
						::reallocate(
						void
						*
						 p, size_t old_size, size_t new_size) 
     { 
  if
								 (old_size 
								>
								 MAX_BYTES 
								&&
								 new_size 
								>
								 MAX_BYTES) 
     { 
  return
										 realloc(p, new_size); 
  } 
   
  if
								 (round_up(old_size) 
								==
								 round_up(new_size)) 
     { 
  return
										 p; 
  } 
   
  void
								*
								 result 
								=
								 malloc(new_size); 
  int
								 copy_size 
								=
								 new_size 
								>
								 old_size 
								?
								 old_size : new_size; 
  memcpy(result, p, copy_size); 
  
  return
								 result; 
  } 
   
  typedef default_alloc_template
						<
						false
						, 
						0
						>
						 alloc; 
  
   /**/
						
								/* 
  * 对以定义好的两个配置器模板进行封装,以使其与STL相容 
  */ 
  template
						<
						typename T
						> 
  class
						 allocator 
     { 
  public
								: 
  typedef        size_t        size_type; 
  typedef        T            value_type; 
  typedef        T
								*
								            pointer; 
  typedef        ptrdiff_t    difference_type; 
  typedef        
								const
								 T
								*
								    const_pointer; 
  typedef        T
								&
								            reference; 
  typedef        
								const
								 T
								&
								    const_reference; 
  
  template
								<
								typename U
								> 
  struct
								 rebind 
     { 
  typedef allocator
										<
										U
										>
										 other; 
  }
								
								; 
  
  allocator() 
								throw
								() 
     { 
  } 
   
  template
								<
								typename U
								> 
  allocator(
								const
								 allocator
								<
								U
								>&
								 ) 
								throw
								() 
     { 
  } 
   
  ~
								allocator() 
								throw
								() 
     { 
  } 
   
  pointer address(reference x) 
								const 
      { 
  return
										 
										&
										 x; 
  } 
   
  const_pointer address(const_pointer x) 
								const 
      { 
  return
										 
										&
										x; 
  } 
   
  T
								*
								 allocate(size_type n, 
								void
								*
								 p 
								=
								 
								0
								) 
     { 
  return
										 n 
										!=
										 
										0
										 
										?
										 static_cast
										<
										T
										*>
										(alloc::allocate(n 
										*
										 
										sizeof
										(T))) : 
										0
										; 
  } 
   
  void
								 deallocate(pointer p, size_type n) 
     { 
  alloc::deallocate(p, n
										*
										sizeof
										(T)); 
  } 
   
  size_type max_size() 
								const
								 
								throw
								() 
     { 
  return
										 (size_t)
										-
										1
										/
										sizeof
										(T); 
  } 
   
  void
								 construct(pointer p, const_reference val) 
     { 
  new
										(p)T(val); 
  } 
   
  void
								 destroy(pointer p) 
     { 
  p
										->~
										T(); 
  } 
  }
						
						; 
  
  } 
   
  #endif  以下是测试文件,现在比较晚了。。没有写一个比较像样的测试,以后有时候再写写。。。
 
				 //
				 sgi_allocator.cpp : 定义控制台应用程序的入口点。 
  // 
   /**/
				
						/* 
  *    模拟SGI STL中allocator的实现,以内存池的形式,构建一个比STD::allocator 
  *    更高效的空间配置器 
  *    作者: Szwolf 
  *    2006.8.3:23.31'暑假@SZU 
  * 
  */ 
  #include 
				"
				stdafx.h
				" 
  #include 
				"
				sgi_allocator.h
				" 
  #include 
				<
				iostream
				> 
  #include 
				<
				vector
				> 
  #include 
				<
				algorithm
				> 
   
  using
				 
				namespace
				 std; 
  
  class
				 test 
     { 
  public
						: 
  friend ostream
						&
						 
						operator
						 
						<<
						 (ostream
						&
						 os, 
						const
						 test
						&
						 x) 
     { 
  return
								 os 
								<<
								 
								"
								test success : )
								"
								 
								<<
								 endl; 
  } 
  }
				
				; 
  
  int
				 _tmain(
				int
				 argc, _TCHAR
				*
				 argv[]) 
     { 
   int
						 a[] 
						=  {
								1
								,
								2
								,
								3
								,
								4
								}
						
						; 
  test b[
						10
						]; 
  vector
						<
						int
						, SGI::allocator
						<
						int
						>
						 
						>
						 vec(a, a 
						+
						 
						4
						); 
  vector
						<
						test, SGI::allocator
						<
						test
						>
						 
						>
						vec2(b, b
						+
						10
						); 
  copy(vec.begin(), vec.end(), ostream_iterator
						<
						int
						>
						(cout, 
						"
						 
						"
						)); 
  cout 
						<<
						 endl; 
  copy(vec2.begin(), vec2.end(), ostream_iterator
						<
						test
						>
						(cout)); 
  cout 
						<<
						 endl; 
  
  return
						 
						0
						; 
  } 一个类似于SGI STL::allocator的空间配置器就这样完成了:)个人感觉在建内存池的过程从其无所不用其极的空间申请方式中受益颇多~~~(呵呵,链表操作又复习了一遍。。)以下是在VS2005中可以正常编译通过的源码:
 http://www.cppblog.com/Files/szwolf/sgi_allocator.rar
   |