分配器
- 一般不建议直接使用分配器,它给容器分配内存,分配器的效率高低会直接影响容器的效率,包括速度和空间
- 所有的分配动作,最终都会调用malloc(),这个函数再根据当前所处的操作系统不同,调用不同的操作系统API,拿到真正的内存
malloc
- operator new()和delete底层都调用malloc()
- malloc分配如下图,蓝色的内存,再搭配一整包东西返回
- 附加的额外空间基本是固定的,如果所需的内存大,额外开销就小,如果所需的内存小,额外开销就大
- 加的东西详细见内存管理
allocator
- VC6所提供的分配器allocator
- 当它要提供内存时调用的是allocate,它调用operator new(),后者再调用malloc
- 当它想要释放内存时,调用deallocate,它再调用operator delete(), 后者调用free
- VC6+的allocator只是以::operator new和::operator delete完成allocate()和deallocate(),没有任何特殊设计。底层是调用c的malloc和free释放和管理内存。这种用法在分配小空间时每次的额外开销都占比很大,VC没有对此做出优化。
//VC6所附加的标准库,其allocator的实现如下
template<class _Ty>
class allocator{
public:
typedef _SIZE size_type;
typedef _PDFT difference_type;
typedef _Ty _FARQ *pointer;
typedef _Ty value_type;
//第一个参数表示想要多少个类型
//第二个参数表示类型的名称是什么,void*可以传入任何类型
pointer allocate(size_type _N, const void*)
{ return (_Allocate((difference_type)_N, (pointer)0))); }
void deallocate(void _FARQ *_P, size_type)
{ operator delete(_P_; }
};
//_Allocate()定义如下
template<class _Ty> inline
_Ty _FARQ *_Allocate(_PDFT _N, _Ty _FARQ *)
{ if(_N < 0 ) _N = 0;
return ((_Ty _FARQ *) operator new((_SIZT) _N * sizeof(_Ty))); }
- allocator< int >(),类模板加上参数后就称为了一个类的名称,直接加小括号就成为一个临时对象
- 临时对象没有名称
//使用allocator,分配512ints
int *p = allocator<int>().sllocate(512, (int*)0);
//构建临时对象,通过deallocate释放已经分配的内存
//要传入参数表示当时申请的内存大小
allocator<int>().deallocate(p, 512);
alloc
-
G2.9中容器的默认分配器是alloc,它对额外开销做了优化
- 额外开销主要是由于每次分配都有头尾两个cookie,占8个字节
- malloc的分配,是针对不同的人来使用的,所以大小不一,但是容器中每个元素的大小都是一致的,所以可以从此入手优化
- alloc设计了16条链表,每一条链表负则不同的区块,第0条链表负则的是8个字节的大小,第6条链表负则的是56个字节的大小,第15条负责的是168个字节的大小
- 所有的容器,当它需要内存时,都向该分配器要内存,所有的容器大小都被调制到8的倍数。比如50调整到56,56是第6条链表负则的,查看该链表是否有内存块,如果没有,它再用malloc去向操作系统要一大块内存来做切分,切出来的东西再用单向链表串起来。这样获得的每一块内存就不带cookie,从而减少了开销。
-
G4.9版容器的分配器又回到了无优化的版本