这里主要说的是具备次配置力的SGI空间配置器。
SGI STL的配置器与众不同,也与标准规范不同,其名称是alloc而不是allocator,而且不接受任何参数。
比如:
Vector<int,std::allocator<int>> iv; //此写法错误
Vector<int,std::alloc> iv;//此写法正确
注意:
SGI STL的每一个容器都已经指定其缺省的空间配置器为alloc。
SGI STL也定义了一个符合部分标准,名为allocator的配置器,但是SGI从未使用过它,也不建议我们使用,主要是因为效率不佳,他只把C++的::operator new和::operator delete做一层薄薄的包装而已。
下面我们要说的是SGI的特殊的空间配置器。
SGI特殊的空间配置器----->std::alloc
我们所习惯的C++内存配置和内存释放的操作是:
先配置内存,再构造对象。
先对象析构,再释放内存。
在这里,为了精密分工,所以决定将这个两个阶段操作区分开来处理。
内存配置操作由alloc::allocator()负责,内存释放操作由alloc::deallocator()负责。
对象构造操作由::construct()负责,对象析构操作由::destroy()负责。
(就是将内存配置,释放和对象构造,析构分开来进行)
配置器定义于<memory>之中,而SGI的<memory>内包含以下两个文件:
#include <stl_alloc.h> //负责内存空间的配置与释放
#include <stl_construct.h> //负责对象内容的构造与析构
如下图:
1.下面一起来看一下构造和析构基本工具:construct() 和 destroy()
以下是<stl_construct.h>的部分内容:
#include <new.h>
template <class T1,class T2>
inline void construct(T1 * P,const T2& value)
{
new(p) T1(value); //placement new 的调用
}
/*
Placement new它用于在给定的内存中初始化对象(注意:此处是已经给定的内存)
Placement new的调用方式是:new(p) T1(value); //(其中p是已经申请好的内存)
*/
//以下是destroy()第一版本,接受一个指针
template <class T>
inline void destroy(T* pointer)
{
poniter-> ~T(); //调用dtor ~T()
}
//以下是destroy()第二版本,接受两个迭代器,此函数设法找出元素的数值型别
//进而利用_type_traits<>求取最适当的措施
template <class ForwardIterator>
inline void destroy(ForwardIterator first,ForwardIterator last)
{
_destroy(first,last,value_type(first));
}
//判断元素的数值型别(value type)是否有trivial destructor
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());
}
//如果元素的数值型别(value type)有non_trivial destructor:
template <class ForwardIterator>
inline void _destroy_aux(ForwardIterator first,ForwardIterator last,_false+type)
{
for(;first<last;++first)
{
destroy(&*first);
}
}
//如果元素的数值型别(value type)有trivial destructor:
template <class ForwardIterator>
inline void _destroy_aux(ForwardIterator,ForwardIterator,_true_type)
{}
//以下是destroy()第二版本针对迭代器为char *和wchar_t*的特化版
inline void destroy(char *,char *){}
inline void destroy(wchar_t*,wchar_t*){}
由以上代码我们可以知道:
(1)construct()函数接受一个指针p和一个初值value,该函数的用途就是将初值设定到指针所指的空间上。(所以此处用的是C++中的placement new)
(2)Destroy()函数有两个版本:
(a)第一个版本接受一个指针,准备将该指针所指的对象析构掉(此处直接调用析构函数即可)。
(b)第二个版本是接受两个迭代器first和last,准备将[first,last)范围内的所有对象析构掉,我们不知道这个范围有多大,万一很大,而每个对象的析构函数都无关痛痒,那么一次次调用个这些无关痛痒的析构函数对效率是一种伤害。因此,这里首先利用value_type()获得迭代器所指对象的型别,再利用_type_traits<T>判断该型别的析构函数是否无关痛痒,若是(_true_type),则什么也不做就结束;若否(_false_type),这才以循环方式巡防整个范围,并在循环中每经历一个对象就调用第一版本的destroy().
注意:这两个作为构造和析构之用的函数被设计为全局函数,符合STL的规范。
STL还规定配置器必须拥有名为construct()和destroy()的两个成员函数。
(然而真正在SGI STL中大显身手的std::alloc的配置器并未遵守这一规则)