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