STL Allocator空间分配器二

STL Allocator空间分配器二

 

今天把SGI提供的Allocator分配器仔细看了下,其设计还是相当精巧的。不过SGI的分配器已经脱离了STL标准,比如它就没有实现construct()destroy()成员函数。

 

简单malloc分配器

对于大于128B的空间直接就是malloc()free()了,没有什么特殊的;不过它还是仿造C++new handler形式设置了一个malloc exception handler;这样就和C++new行为相一致了,你可以设置malloc失败时的exception handler

 

Newdelete的分离

当对一个对象调用newdelete时,这两个操作都包含了两个阶段的动作:调用operator new分配空间,然后调用该对象的构造函数;调用该对象的析构函数,然后调用operator delete释放空间;SGI的分配器将这两步做了分离,内存分配和释放由函数alloc::allocate()alloc::deallocate()负责;物体构造和析构由函数construct()destroy()负责。

 

在调用destroy()函数同时释放n个对象(假设类型为T)时,SGI提供了方法可以判定对象是否有non-trivial destructor,如果没有则不必要循环为每个对象调用T::~T(),以提高效率,贴上源码,以便查看:

template <class T1, class T2>

inline void construct(T1* p, const T2& value) {

  new (p) T1(value);

}

template <class T>

inline void destroy(T* pointer) {

    pointer->~T();

}

template <class ForwardIterator>

inline void // 具有non trivial destructor

__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {

  for ( ; first < last; ++first)

    destroy(&*first);

}

template <class ForwardIterator>

inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_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());

}

template <class ForwardIterator>

inline void destroy(ForwardIterator first, ForwardIterator last) {

  __destroy(first, last, value_type(first));

}

这需要借助于Traits编程技法来完成(原书3.7节)的:

typedef typename __type_traits<T>::has_trivial_destructor  trivial_destructor;

首先使用value_type()获取迭代器指向的物体类型,然后使用__type_traits<T>查看T是否有non-trivial destructor

 

简单分配器 simple_alloc

SGI为这原始分配器malloc和次级分配器alloc所作的一层简单封装;

 

次级分配器alloc

对于小于128B的请求采用了次级分配器;说白了就是SGI维护一个内存池来处理这些请求,以保证效率。它会将请求的字节数n圆整到8的倍数,比如如果请求的是14B的空间,其实获得的是16B;从这也可以得出SGI一共有16个链表需要维护,每个链表对应一个分配级别,对应的内存块大小分别是:81624128

为了不浪费存储空间,链表是一个union结构,像这样:

       union OBJ{

              union OBJ *free;

              char *data[1];

       };

free指针指向的是free链表中下一个空闲内存块,如果一个内存块被分配出去,就交给用户程序维护了,那么该块就没有必要继续维护了,知道再次收回(通过 free())。

分配和回收函数allocate()deallocate()就是简单的链表操作了,没有特别的地方。

比较复杂的就是内存池的维护函数chunk_alloc()函数,它最终还是需要通过malloc()来申请内存新的空间。

 

辅助函数

最后是几个操作为初始化空间的辅助函数,其内部基本都是通过调用全局construct()函数完成的。

 

对别人不是问题的问题

读到最后一直有个疑惑就是allocator没有实现construct()函数,那么它和容器是如何协同工作的呢?搜了搜源文件才发现,原来各容器都会显式调用全局函数construct()construct函数实际调用placement new),比如下面是list容器模板的一段代码:

 

typedef simple_alloc<list_node, Alloc> list_node_allocator;

link_type get_node() { return list_node_allocator::allocate(); }

void put_node(link_type p) { list_node_allocator::deallocate(p); }

  link_type create_node(const T& x) {

    link_type p = get_node();

    __STL_TRY {

      construct(&p->data, x);

    }

    __STL_UNWIND(put_node(p));

    return p;

  }

  void destroy_node(link_type p) {

    destroy(&p->data);

    put_node(p);

  }

 

你可能感兴趣的:(STL Allocator空间分配器二)