STL源码剖析—空间配置器

allocator

由于对象的创建分为分配内存和调用构造函数两部分,STL allocator使用 alloc::allocate()来分配内存,::construct()构造对象。

construct

construct()函数只有一个泛化的版本,destroy()函数有一个泛化的针对迭代器的版本,__destroy_aux()根据是否需要调用析构函数进行了特化。

//构造对象
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();
}

//把半开半闭区间[first, last)内的所有元素析构掉
//如果该元素的析构函数是trivial的,则什么也不做   trivial表示可以不调用析构函数
//如果该元素的析构函数是non-trivial的,则依序调用其析构函数
template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last) {
 __destroy(first, last, value_type(first));
}


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());
}

//该元素的析构函数是trivial的,则什么也不做
template <class ForwardIterator> 
inline void __destroy_aux(ForwardIterator, ForwardIterator, _true_type) 
{
 //no-op
}

//该元素的析构函数是non-trivial的,则依序调用其析构函数
template <class ForwardIterator>
inline void
__destroy_aux(ForwardIterator first, ForwardIterator last, _false_type) 
{
 for ( ; first < last; ++first)
   destroy(&*first);
}


//如果区间内的元素类型为char或wchar_t,则destroy什么也不做
inline void destroy(char*, char*) {  }
inline void destroy(wchar_t*, wchar_t*) {  }

alloc

STL使用了两级内存分配,当申请内存较大时(大于128字节)调用malloc()分配内存。较小时从内存池中分配。

包装接口

定义配置器时均使用此包装接口定义。

//申请内存较大时直接使用malloc
//包装接口,使用传入的Alloc分配内存,Alloc默认第二级
template <class T, class Alloc>
class simple_alloc {
   public:
    static T* allocate(size_t n) {
        return n == 0 ? 0;
        (T*)Alloc::allocate(n * sizeof(T));
    }
    static T* allocate(void) { return (T*)Alloc::allocate(sizeof(T)); }
    static void deallocate(T* p, size_t n) {
        if (0 != n) Alloc::deallocate(p, n * sizeof(T));
    }
    static void deallocate(T* p) { Alloc::deallocate(p, sizeof(T)); }
};
第一级分配器
//申请内存较大时直接使用malloc
class malloc_alloc {
   public:
    static void* allocate(size_t n) { return malloc(n); }
    static void* deallocate(void* p, size_t) { free(p); }
    static void* reallocate(void* p, size_t, size_t new_sz) {
        return realloc(p, new_sz);
    }
};
第二级分配器

第二级配置器维护16个自由区块链表,分别管理8-128字节的区块,每次从适合的区块中分配内存。

//第二级配置器
class default_alloc {
   private:
    enum { ALIGN = 8 }; //区块大小是8的倍数
    enum { MAX_BYTES = 128 };  //区块最大大小
    enum { NFREELISTS = MAX_BYTES / ALIGN }; //自由区块链表个数

    union obj {
        union obj* free_list_link;
        char client_data[1];
    };
    static obj* free_list[NFREELISTS];

    static char* start_free;
    static char* end_free;
    static size_t heap_size;

    static size_t ROUND_UP(size_t bytes) {  //调整为8的倍数
        return ((bytes + ALIGN - 1) & ~(ALIGN - 1));
    }
    static size_t FREELIST_INDEX(size_t bytes) {  //根据区块大小访问对应链表
        return (((bytes) + ALIGN - 1) / ALIGN - 1);
    }
    static void* refill(size_t n);  //返回一个对象并填充新的区块
    static char* chunk_alloc(size_t size, int& nobjs);  //配置 nobjs * size 大小的空间

   public:
    static void* allocate(size_t n);
    static void deallocate(void* p, size_t);
    static void* reallocate(void* p, size_t, size_t new_sz);
};

这里主要分析一下chunk_alloc()函数

char *default_alloc::chunk_alloc(size_t size, int &nobjs) {
    char *result;
    size_t total_bytes = size * nobjs;
    size_t bytes_left = end_free - start_free;
    if (bytes_left >= total_bytes) {  //内存池剩余空间满足分配要求
        result = start_free;
        start_free += total_bytes;
        return result;
    } else if (bytes_left >= size) {  //不能完全满足,有多少分配多少
        nobjs = bytes_left / size;
        total_bytes = size * nobjs;
        result = start_free;
        start_free += total_bytes;
        return result;
    } else {  //一个区块也无法满足
        size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
        if (bytes_left > 0) {  //将剩余的零头插入相应的链表
            obj *my_free_list = free_list[FREELIST_INDEX(bytes_left)];
            ((obj *)start_free)->free_list_link = my_free_list;
            my_free_list = (obj *)start_free;
        }
        start_free = (char *)malloc(bytes_to_get);  //给内存池分配内存
        if (0 == start_free) {  //如果已经分配不出内存
            obj *my_free_list, *p;
            for (int i = size; i < MAX_BYTES; i += ALIGN) {
                my_free_list = free_list[FREELIST_INDEX(i)];  //看较大的区块链表中是否由空闲区块
                p = my_free_list;
                if (0 != p) {
                    my_free_list = p->free_list_link;  //摘出一个区块
                    start_free = (char *)p;
                    end_free = start_free + i;
                    return chunk_alloc(size, nobjs);
                }
            }
            end_free = 0;
            start_free = (char *)malloc_alloc::allocate(bytes_to_get);
        }
        heap_size += bytes_to_get;
        end_free = start_free + bytes_to_get;
        return chunk_alloc(size, nobjs);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值