allocate:分配内存
deallocate:释放内存
construct:在已申请的内存上构造对象
destroy:析构对象
address:取某个对象的地址
const_address:常版本地址
max_size:返回可申请的最大空间
std::allocator,这个分配器类只是对原始的new和delete做了一层包装,效率很低不建议使用。
std::alloc:这个才是真正投入使用的
构造很容易,直接用placement new就行了
template<class T1, class T2> inline void construct(T1 *p, const T2& value) { new (p) T1(value); // placement new,在以分配的内存上构造 }
析构就比较麻烦了,有好几个版本,这里看最简单的,直接调用析构函数析构
template<class T> inline void deconstruct(T *pointer) { pointer->~T(); //调用析构 }
这一块涉及到操作系统的一些知识。为了简单,我们不考虑多线程的各种同步问题。
最简单的内存分配,就是用使用new和delete,底层是c的malloc和free。但是,如果每次申请的内存都是很小一块,会导致内存碎片的问题。
STL设置了两级的分配器。如果分配的内存很大,那么就直接使用一级分配器。如果很小,就使用内存池,让二级分配器来分配内存。
一级分配器非常简单,主要功能如下
二级分配器用来分配小于128k的内存,大于这个数的内存交给一级分配器完成。
二级分配器用内存池来维护一系列内存块。首先,分配器保存了一个freeList,这个freeList是一个链表数组,保存了8的倍数的内存块,并以链表维护。
解释一下,freeList的定义是这样的:void *freeList[16],其中freeList[0]保存一个链表,这个链表每个节点的大小都是8个字节,同理,freeList[1]保存一个节点为16字节大小的链表。那这样第16个就是保存128字节大小的内存块。
有个问题是链表的next指针,如果单独在每个节点中加个next,开销还是很大的。这里我们可以用union,当内存节点在freeList中的时候,就当作一个next指针。当该内存节点被申请后,就当作真实数据。
还有一个需要注意的是,我们会对申请的字节做一个8字节对齐(便于内存的分配)。比如我们申请2个字节,会自动对齐到8字节;申请15字节会对齐到16字节。
这是几个全局函数,在大规模内存上构造对象。
uninitialized_copy
// 将first和last之间的对象产生一个拷贝,并用于构造result到result+(last-frist)之间的内存。 uninitialized_copy(first, last, result);
uninitialized_fill
// 用T构造first到last之间的内存 uninitialized_fill(first, last, T);
uninitialized_fill_n
// 用T构造first到first + n之间的内存 uninitialized_fill_n(first, n, T);