空间配置器隐藏在一切组件之后。
1.1 设计一个简单的空间配置器
- 根据STL的规范,以下是allocator的必要接口:
allocator::value_type
allocator::pointer
allocator::const_pointer
allocator::reference
allocator::const_reference
allocator::size_type
allocator::difference_type
allocator::rebind // 一个嵌套的(nested)class template。class rebind<U>拥有唯一成员other,是一个typedef,代表alloctor<U>
allocator::allocator() // default constructor
allocator::allocator(const allocator&) // copy constructor
template <class U>allocator::allocator(const allocator<U>&) //泛化的default constructor
allocator::~allocator() // default constructor
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::max_size() const // 返回可成功配置的最大量
void allocator::construct(pointer p, const T& x) // 等同于new(const void*) p) T(x)
void allocator::destroy(pointer p) // 等同于p->~T()
1.2 一个简单的allocator源代码
// filename : qyalloc.h
#ifndef __QYALLOC__
#define __QYALLOC__
// placement new是operator new的一个重载版本,只是我们很少用到它。如果你想在已经分配的内存中创建一个对象,使用new是不行的。也就是说placement new允许你在一个已经分配好的内存中(栈或堆中)构造一个新的对象。原型中void*p实际上就是指向一个已经分配好的内存缓冲区的的首地址。
// placement new的作用就是:创建对象(调用该类的构造函数)但是不分配内存,而是在已有的内存块上面创建对象。用于需要反复创建并删除的对象上,可以降低分配释放内存的性能消耗。请查阅placement new相关资料。
#include <new> // placement new 要包含此文件,声明了一个void *operator new( size_t, void *p ) throw() { return p; }
#include <cstddef> // for ptrdiff_t, size_t
#include <cstdlib> // for exit()
#include <climits> // for UINT_MAX
#include <iostream> // for cerr
namespace QY{
// 分配空间(operator new)
template <class T>
inline T* _allocate(ptrdiff_t size, T*){
std::set_new_handler(0);
T *tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
if (tmp == 0)
{
std::cerr << "out of memory" << std::endl;
exit(1);
}
return tmp;
}
// 回收空间(operator delete)
template <class T>
inline void _deallocate(T* buffer){
::operator delete(buffer);
}
// 在指定内存上构造一个对象(new(pMyClass)MyClass();)
template <class T1, class T2>
inline void _construct(T1* p, const T2& value){
new(p) T1(value); // 创建。placement new. 调用 ctor of T1, 即new(pMyClass)MyClass();
}
// 析构对象
template<class T>
inline void _destroy(T* ptr){
ptr->~T();
}
// 按allocator标准,定义结构
template <class T>
class allocator{
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
// 重新绑定分配器(rebind allocator of type U)
template <class U>
struct rebind
{
typedef allocator<U> other;
};
pointer allocate(size_type n, const void* hint=0){
return _allocate((difference_type)n, (pointer)0);
}
void deallocate(pointer p, size_type n){
_deallocate(p);
}
void construct(pointer p, const T& value){
_construct(p, value);
}
void destroy(pointer p){
_destroy(p);
}
pointer address(reference x){
return (pointer)&x;
}
const_pointer address(const_reference x){
return (const_pointer)&x;
}
size_type max_size() const{
return size_type(UINT_MAX / sizeof(T));
}
};
} // end of namespace QY
#endif // __QYALLOC__
1.3 使用这个allocator
#include "qyalloc.h"
#include <vector>
#include <iostream>
using namespace std;
int main(){
int ia[5] = {0, 1, 2, 3, 4};
unsigned int i;
vector<int, QY::allocator<int> > iv(ia, ia+5);
for(i=0; i<iv.size(); i++)
cout << iv[i] << ' ';
cout << endl;
return 0;
}
1.4 SGI标准的空间配置器(std::allocator)
符合部分标准,效率不佳,不建议使用。
1.5 SGI特殊的空间配置器(std::alloc)
class Foo{ ... };
Foo* pf = new Foo; // 配置内存,然后构造对象
delete pf; // 将对象析构,然后释放内存
new: (1)调用 ::operator new 配置内存;
(2)调用 Foo::Foo() 构造对象内容。
delete: (1)调用 Foo::~Foo() 将对象析构;
(2)调用 ::operator delete 释放内存。
为了精密分工,STL allocator 将两阶段操作区分开来。
alloc::allocate()负责内存配置操作;
alloc::deallocate()负责内存释放操作;
::construct()负责对象构造操作;
::destroy()负责对象析构操作。