1 简述
STL其他组件都是存放在空间配置器配置的空间中,此处空间可以是内存,也可以是磁盘或其他辅助存储介质。
allocator负责内存的分配和释放,以及负责对象的构造和析构,两个操作时分开的。
每个容器都已经制定了默认的空间配置器Alloc,如下图所示。
若要使用自己的空间配置器则必须vector<int,my_alloc> mv;
2 标准接口
// 以下几种自定义类型是一种type_traits技巧,暂时不需要了解
allocator::value_type
allocator::pointer
allocator::const_pointer
allocator::reference
allocator::const_reference
allocator::size_type
allocator::difference
allocator::rebind
// 一个嵌套的(nested)class template,class rebind<U>拥有唯一成员other,那是一个typedef,代表allocator<U>
allocator::allocator() // 默认构造函数
allocator::allocator(const allocator&) // 拷贝构造函数
template <class U>allocator::allocator(const allocator<U>&) // 泛化的拷贝构造函数
allocator::~allocator() // 析构函数
pointer allocator::address(reference x) const
// 返回某个对象的地址,a.address(x)等同于&x
const_pointer allocator::address(const_reference x) const
// 返回某个const对象的地址,a.address(x)等同于&x
pointer allocator::allocate(size_type n, const void* = 0)
// 配置空间,足以存储n个T对象。第二个参数是个提示。实现上可能会利用它来增进区域性(locality),或完全忽略之
void allocator::deallocate(pointer p, size_type n)// 释放先前配置的空间
size_type allocator:maxsize() const// 返回可成功配置的最大量
void allocator::construct(pointer p, const T& x)
//在p指向位置 调用对象的构造函数,等同于 new((void*)p) T(x)
void allocator::destroy(pointer p)// 调用对象的析构函数,等同于 p->~T()
3 空间配置器申请的空间均仅仅是空间(如仅分配内存而未构造对象)
这里说明几种申请内存的区别
A *a = (A*)(::operator new A ((size_t) (size * sizeof(A)));
调用全局operator new(size_t size)仅仅申请内存,然后返回指针。allocate中使用。void *mem = operator new A (sizeof(T1),p);
借用p指向的内存处大小为sizeof(T1)的内存,返回void*指针。A *a = new A (...);
①申请内存(与operator new相同)
②调用A(…)构造对象
③返回指针
//Complex* pc=new Complex(1,2)相当于
try{
void* mem = operator new(size(Complex));
Complex* pc=static_case<Complex*>(mem);
pc->Complex()::Complex(1,2);
}
catch(std::bad_alloc){
... }
new(p) T1(value);
为已申请内存*p构造T1。construct时使用。相当于
try{
void* mem = operator new(size(T1),p);
p* pc=static_case<T1*>(mem);
pc->T1()::T1();
}
catch{
... }
4 SGI空间配置器
SGI的空间配置器名称为alloc,而非allocator,不接受任何参数。
vector<int,allocator<int>> iv;
vector<int,alloc> iv;
我们所习惯的c++内存配置操作和释放操作是使用new和delete来完成的:
class Foo {
... };
Foo* pf = new Foo; // 配置内存,然后构造对象
delete pf; // 将对象析构,然后释放内存
如上一部分所说,其中的new 操作符(new operator)包含两阶段操作:
(1)调用operator new配置内存
(2)调用Foo::Foo( )构造函数构造对象内容。
delete操作符同样也包含两阶段操作:
(1)调用Foo::~Foo( )析构函数将对象析构。
(2)调用operator delete释放内存
STL allocator 将这两阶段操作区分开来。
内存配置操作由 alloc::allocate() 负责,内存释放操作由 alloc::deallocate() 负责;
对象构造操作由 ::construct() 负责,对象析构操作由 ::destroy() 负责。
//负责内存空间的配置与释放;
<stl_alloc.h>//文件中定义了一、二两级配置器,彼此合作,配置器名为alloc。
//负责对象内容的配置与释放
<stl_construct.h>//全局函数construct()和destroy(),负责对象的构造和析构。
//用来填充fill或复制copy大块内存数据
<stl_uninitialized.h>//uninitialized_copy();uninitialized_fill();uninitialized_fill_n
uninitialized_copy(first, last, result) //将[first,last)范围内的对象复制到result处;
uninitiated_fill(first, last, X) //将[first,last)范围内的内存用对象X的副本填充;
uninitiated_fill_n(first, n, X) //将first开始的n个连续的内存空间用X的副本填充;
4.1 构造与析构工具:construct()和destroy()
- construct()仅仅负责构造对象,需要提前用allocate()申请内存。两操作合起来相当于new。
construct()接受一个指针p和初值value,该函数将初值设定到所指空间上; - destroy()仅仅负责析构对象,需要之后用deallocate()释放内存。两操作合起来相当于delete。
destroy()有两个版本:
1、接收一个指针,准备将该指针所指之物析构掉。直接调用该对象的析构函数;
2、接收first和last两个迭代器,准备将[first,last)范围内的所有对象析构掉。
利用value_type()获取迭代器所指对象的类型,利用__type_traits判断该类型的析构函数是否无关痛痒。若是__true_type,则什么也不做,反之若是__false_type则循环访问,对每个对象调用析构函数。
#ifndef __SGI_STL_INTERNAL_CONSTRUCT_H
#define __SGI_STL_INTERNAL_CONSTRUCT_H
#include <new.h> // 欲使用 placement new,需先含入此檔
template <class T1, class T2>
inline void construct(T1* p, const T2& value) {
new (p) T1(value); // placement new; 调用 T1::T1(value);
}
//