《stl源码剖析》-- 空间配置器

一、总述

一般而言,我们所习惯的C++内存的配置和释放是这样的:

class Foo {
//定义
};
Foo *pf = new Foo;
delete pf;
其中new操作包含两个步骤:(1)调用operator new配置内存;(2)调用Foo()构造对象内容

delete算式也包括两个步骤:(1)调用~Foo()将对象析构;(2)调用operator delete

为了精密分工,stl将这两个步骤区分开来,内存配置操作由allocate()负责,内存释放操作由deallocate()负责;

对象构造操作由construct()负责,对象析构操作由destroy()负责。

stl的空间配置器本质上来说就是为了实现这四个函数。

二,construct和destroy的实现

这两个函数在stl_construct.h中实现,代码注释如下:

//construct函数,构造p指向的内存对象
template <class T1, class T2>
inline void construct(T1* p, const T2& value) {
  new (p) T1(value);
}


//destroy函数,析构pointer指向的内存对象
template <class T>
inline void destroy(T* pointer) {
    pointer->~T();
}

//下面三个函数用来实现第一个destroy函数的重载版本
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {
  for ( ; first < last; ++first)
    destroy(&*first);	//调用第一个destroy函数
}

template <class ForwardIterator> 
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}


//根据T类型是否含有trival destructor,调用不同的__destroy_aux重载函数
//trival destrutor的定义
//http://stackoverflow.com/questions/8190879/what-is-a-non-trivial-destructor-in-c
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());
}


//重载的destroy版本
template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last) {
  __destroy(first, last, value_type(first));
}

//特化的destroy函数
inline void destroy(char*, char*) {}
inline void destroy(wchar_t*, wchar_t*) {}

三、allocate和deallocate的实现

stl定义了两个类模板,实现了两种allocate和deallocate,称为双层级配置器。第一级配置器直接使用malloc()和free(),第二级配置器则视情况采取不同的策略:

当配置区块超过128bytes时,视之为“足够大”,便调用第一级配置器;当配置区块小于128bytes时,视之为“过小”,为了降低额外负担,便采取复杂的memory pool

整理方式,而不再求助于第一级配置器。

1,stl第一级配置器

SGI STL第一级配置器
template<int inst>
clas __malloc_alloc_template { ... };
其中:
1,allocate()直接使用malloc()。
2,模拟c++的set_new_handler()以处理内存不足的情况
//第一级配置器,直接调用malloc和free向堆空间申请释放内存
//需要多少空间就malloc多少
#if 0
    #include <new>
    #define __THROW_BAD_ALLOC   throw bad_alloc
#elif !defined(__THROW_BAD_ALLOC)
    #include <iostream.h>           //.h的头文件没有定义命名空间
    #define __THROW_BAD_ALLOC cerr << "out of memory" << endl; exit(1)
#endif

template <int inst>
class __malloc_alloc_template {
    private://下面三个函数处理内存不足的情况						
        static void *oom_malloc(size_t);		
        static void *oom_realloc(void *, size_t);
        static void (* __malloc_alloc_oom_handler)();   //
    public:
        static void *allocate(size_t n)			//分配内存,直接调用malloc
        {
            void *result = malloc(n);

            if (0 == result)
                result = oom_malloc(n); //调用oom_malloc,该函数会循环调用内存不足处理函数(__malloc_alloc_oom_handler)
            return result;
        }

        static void deallocate(void *p, size_t /* n */)	//释放内存,直接调用free
        {
            free(p);
        }
        
        static void *reallocate(void *p, size_t /* oldsize */, size_t newsize)	//直接调用realloc
        {
            void *result = realloc(p, newsize);
            if (0 == result)
                result = oom_realloc(p, newsize);   //oom_realloc会循环调用处理函数(__malloc_alloc_oom_handler)
            return result;
        }

        //仿真c++的set_new_handler()函数
        static void (*set_malloc_handler(void (*f)))()
        {
            void (*old)() = __malloc_alloc_oom_handler;
            __malloc_alloc_oom_handler = f;
            return old;
        }
};

//定义类的成员变量和成员函数

template <int inst>
void (*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;    //定义类的静态成员变量 

template <int inst>
void * __malloc_alloc_oom_handler<inst>::oom_malloc(size_t n)
{
    void (*my_malloc_handler)();
    void *result;

    for (;;) {
        my_malloc_handler = __malloc_alloc_oom_handler;
        if (0 == my_malloc_handler) 
            __THROW_BAD_ALLOC;          //宏定义
        
        (*my_malloc_handler)();
        result = malloc(n);
        if (0 != result)
            return result;
    }
}


template <int inst>
void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t newsize)
{
    void (*my_malloc_handler)();
    void *result;

    for (;;) {
        my_malloc_handler = __malloc_alloc_oom_handler;
        if (0 == my_malloc_handler)
            __THROW_BAD_ALLOC;

        (*my_malloc_handler)();
        result = realloc(p, newsize);
        if (0 != result)
            return result;
    }
}

//实例化一个类模板
typedef __malloc_alloc_template<0> malloc_alloc;

2,stl第二级配置器

SGI STL第二级配置器
template <bool threads, int inst>
class __default_alloc_template { ... };
其中:
1.维护16个自由链表,负责16种小型区块的次配置能力。
内存池以malloc配置而得。如果内存不足,转调用第一级配置器。
2.如果需求区块大于128bytes,就转调用第一级配置器。

stl第二级配置器含有两个关键结构体:空闲链表(free-list)和内存池。
为了方便管理,SGI第二级配置器会主动将任何小额区块的内存需求量上调至8的倍数,并维护16个free-lists,各自管理大小分别为
8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128bytes的小额区块,如图所示:

内存池结构如下图所示:



内存分配步骤如下:
1,如果要分配的内存大于128bytes,直接调用第一级配置器的allocate(nbytes)(malloc_alloc::allocate),返回结果
2,否则寻找nbytes对应的空闲链表下标,并判断该数组元素是否为空,
如果不为空,返回第一块内存,并修改该数组项的值,如下图所示:


如果为空,则从内存池中分配出一块内存,放入对应的数组项中,然后返回第一块内存,如下图所示:

变为


3,如果内存池也为空,则调用malloc为内存池分配空间,继续执行第2步。
4,如果第三歩的malloc出错,则调用第一级配置器的malloc出错处理函数。
内存分配完成。

内存释放步骤如下:
1,如果要释放的内存大小超过128bytes,调用第一级配置器的deallocate
2,否则,根据要释放内存的大小,加入到合适的空闲链表中,如下图所示:

变为:

明白了分配和释放内存的思路,代码不难看懂。

三、stl源码中如何使用配置器

stl源码中不直接使用第一级配置器或第二级配置器的名字,而是使用一个别名alloc,如果定义了__USE_MALLOC宏,则alloc是第一级配置器的别名,
否则alloc是第二级配置器的别名。
#ifdef __USE_MALLOC
typedef __malloc_alloc_template<0> malloc_allloc;//第一级配置器
typedef malloc_alloc alloc;

#else
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc; //第二级配置器

stl还为alloc定义了一个包装类simple_alloc
template<class T, class Alloc>
class simple_alloc {
public:
    static T* allocate(size_t);
    static T* allocate(void);
    static void deallocate(T*, size_t);
    static void deallocate(T*);
};

simple_alloc仅仅只是一个包装类,他内部直接调用alloc的对应函数。
下面来看vector 和 deque的实际运用方式:
template <class T, class Alloc=alloc>   //默认配置器是alloc
class vector {
    typedef simple_alloc<T, Alloc> data_alloctor;
    data_alloctor::allocate(n); //配置n个元素
};

template<class T, class Alloc=alloc, size_t BufSiz=0>
class deque {
    typedef simple_alloc<T, Alloc> data_alloctor;
    typedef simple_alloc<T*, Alloc> map_alloctor;
    data_alloctor::allocate(n);//配置n个元素
};

实际使用的是simple_alloc,simple_alloc会调用alloc,而alloc可能是第一级配置器,也可能是第二级配置器。
所以最终起作用的还是第一级配置器或第二级配置器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值