简介:SGI STL的配置器与标准规范不同,名称为alloc而不是allocator,而且不接受任何参数。SGI标准的空间配置器std::allocator和SGI特殊的空间配置器std::alloc。其中allocator配置器,SGI从来没用过它,因此也不建议我们使用。原因主要是因为效率不佳。仅仅只是把C++的 ::operator new 和 ::operator delete 做一层小包装而已。因此下面我们主要介绍SGI特殊的空间配置器std::alloc。
一般而言在C++中内存配置操作和释放都要经过两个阶段:
class F{…};
F* p = new F; //内存配置,然后构造对象
delete p; //将对象析构,然后释放
new算式内包含两个阶段操作:(1)调用 ::operator new配置内存;(2)调用F::F()构造对象
delete算式包含两个阶段操作:(1)调用F::~F()将对象进行析构;(2)调用 ::operator delete释放内存。
但是在STL中,为了精密分工将这两个阶段操作分开。内存配置操作由alloc::allocate()负责,内存释放操作由alloc::allocate()负责;对象构造操作由::construct()负责,对象析构由::destory()负责。
本次我们只阐述构造和析构。下面为构造和析构的基本工具:
#include<new.h>
template<class T1, class T2>
inline void construct(T1* p, const T2& value){
new (p) T1(value); //placement new;调用T1::T1(value)
}
//destory()第一个版本,接受一个指针
template<class T>
inline void destory(T* p){
p->~T(); //调用dtor ~T()
}
//destory()第二个版本,接受两个迭代器。此函数设法找出元素的数值型
//别,进而利用__type_traits<>求最适当措施
template<class ForwardIterator>
inline void destory(ForwardIterator first, ForwardIterator last){
__destory(first, last, value_type(first));
}
//判断元素型别(value type)是否具有trivial destructor
template<class ForwardIterator, class T>
inline void __destory(ForwardIterator first, ForwardIterator last, T*){
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
__destory_aux(first, last, trivial_destructor());
}
//如果元素的数值型别(value type)有non-trivial destructor..
//具备的是有用的析构函数,那么函数内部调用destroy()
template<class ForwardIterator>
inline void
__destory_aux(ForwardIterator first, ForwardIterator last, __false_type){
for( ; first < last; ++first)
destory(&*first);
}
//如果元素的数值型别(value type)有trivial destructor
//有无用的析构函数,函数体无操作
template<class ForwardIterator>
inline void __destory_aux(ForwardIterator, ForwardIterator, __true_type){
}
//以下是destory()第二个版本针对迭代器为char* 和 wchar_t* 的特化
inline void destory(char*, char*){}
inline void destory(wchar_t*, wchar_t*){}
placement new 使得你在一个已经分配好的内存中(栈或堆)构造一个新的对象,原型中 p 实际上就是指向一个已经分配好的内存缓冲区的首地址。STL 借助C++中的 placement new 来提高效率,因为使用 new 操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常。借助 placement new 就可以解决这个问题,我们构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数,而且不会出现在程序运行中途出现内存不足的异常。
上述constructor()接受一个指针p和一个初值value,该函数的用途就是将初值设定到指针所指定的空间上。
destory()有两个版本,第一个很简单。第二个版本接受first和last两个迭代器,准备将[first, last)范围内的所有对象析构掉,但我们不知道这个空间范围有多大,而每个对象的析构函数都无关痛痒(trivial destory),那么多次调用这些无关痛痒的析构函数,对于效率来说是一种伤害。因此先调用value_type()获得迭代器所指对象的型别,再利用 __type_traits判断析构函数是否无关痛痒。若是(true_type),则什么也不做就结束;若否 (__fase_type),这才循环方式寻访整个范围,并在循环中每经历一个对象就调用第一个版本的destory()。