1、 什么是空间配置器
在我看来就是STL容器后面默默工作进行内存管理和分配以及回收的的一个组件,为什么不叫内存配置器?STL源码剖析里说到:
因为空间不一定是内存,空间也可以是磁盘或其他辅助的存储介质。
2、空间配置器的标准接口有哪些
- allocator::value_type
- allocator::pointor
- allocator::const_pointer
- allocator::reference
- allocator::const_reference
- allocator::size_type
- allocator::difference_type
- allocator::rebind 一个嵌套的class template
- allocator::allocator()
- allocator::allocator(allocator&)
- allocator::~allocator()
- template allocator::allocator(const allocator&)
- pointer allocator::address(reference x) const
- const_pointer allocator::address(const_reference x) const
- pointer allocator::allocate(size_type n, const void* = 0)
- void allocator::deallocate(pointer p, size_type n)
- size_type allocator::max_size() const
- void allocator::construct(pointer p, const T& x) == new((void*)p) T(x)
- void allocator::destroy(pointer p) == p->~T()
3、实现一个简单的空间配置器
#ifndef CALLOCATOR_H
#define CALLOCATOR_H
#include <new>
#include <cstddef>
#include <cstdlib>
#include <climits>
#include <iostream>
template <class T>
class CAllocator
{
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;
template <class U>
struct rebind
{
typedef CAllocator<U> other;
};
pointer allocate(size_type n, const void* hint = 0)
{
return (pointer)(::operator new(n * sizeof(T)));
}
void deallocate(pointer p, size_type n){ ::operator delete(p); }
void construct(pointer p, const T& x) { new(p) T(x);}
void destroy(pointer p) { p->~T(); }
pointer address(reference x) {return (pointer)&x; }
size_type max_size() const { return size_type(UINT_MAX / sizeof(T)); }
const_pointer const_address(const_pointer x){ return (const_pointer)&x; }
CAllocator(){};
~CAllocator(){};
protected:
private:
};
#endif // CALLOCATOR_H
#include <iostream>
#include "CAllocator.h"
#include <vector>
int main()
{
int a[5] = {0,1,2,3,4};
std::vector<int, CAllocator<int> > av(a, a+5);
for(auto i: av)
std::cout << i << ' ';
std::cout << std::endl;
return 0;
}
3、SGI STL的配置器与标准规范不同
SGI STL仍然提供了一个标准的配置接口,叫simple_alloc,而SGI STL的配置器叫alloc而非allocator,而且不接受任何参数,例如:
vector<int, std::allocator<int>> iv;
vector<int, std::alloc> ic;
但是SGI STL里提供的配置器效率更加好!
4、一般C++的内存配置的操作和释放过程:
例如
class A{...};
A* a = new A;
delete a;
new的操作:
1. 调用::operator new 进行配置内存;
2. 调用A::A()进行构造;
delete的操作:
1. 调用A::~A()进行析构;
2. 调用::operator delete 进行内存释放;
而STL的空间配置器恰恰把这两阶段区分开来,
1. 内存的分配由alloc::allocate()负责
2. 内存的释放由alloc::deallocate()负责
3. 对象的构造由::construct()负责
4. 对象的析构由::destroy()负责
按照STL的标准
配置器应该定义于之中,而SGI内含两个文件
#include <stl_alloc.h> //负责内存的配置和释放
#include <stl_construct.h> //负责对象的构造和析构
#include <stl_uninitialized.h> //定义了一些填充函数
#include <new.h> //使用placement new
template <class _T1, class _T2>
inline void _Construct(_T1* __p, const _T2& __value) {
new ((void*) __p) _T1(__value); //调用placement new;
}
template <class _T1>
inline void _Construct(_T1* __p) {
new ((void*) __p) _T1();
}
template <class _Tp>
inline void _Destroy(_Tp* __pointer) {
__pointer->~_Tp(); //调用对象的析构
}
//判断区间元素里是否有没有必要调用对象的析构函数
template <class _ForwardIterator, class _Tp>
inline void
__destroy(_ForwardIterator __first, _ForwardIterator __last, _Tp*)
{
typedef typename __type_traits<_Tp>::has_trivial_destructor
_Trivial_destructor;
__destroy_aux(__first, __last, _Trivial_destructor());
}
//如果有必要就调用析构函数
template <class _ForwardIterator>
void
__destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type)
{
for ( ; __first != __last; ++__first)
destroy(&*__first);
}
//没有必要就不调用析构函数,大大提升效率
template <class _ForwardIterator>
inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {}
template <class _ForwardIterator>
inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) {
__destroy(__first, __last, __VALUE_TYPE(__first));
}
template <class _T1, class _T2>
inline void construct(_T1* __p, const _T2& __value) {
_Construct(__p, __value);
}
template <class _T1>
inline void construct(_T1* __p) {
_Construct(__p);
}
template <class _Tp>
inline void destroy(_Tp* __pointer) {
_Destroy(__pointer);
}
template <class _ForwardIterator>
inline void destroy(_ForwardIterator __first, _ForwardIterator __last) {
_Destroy(__first, __last);
}
空间的配置与释放
#ifdef __USE_MALLOC
...
typedef __malloc_alloc_template<0> malloc_alloc;
typedef malloc_alloc alloc; //第一级配置器
...
#else
...
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;//第二级配置器
#endif
为了使SGI配置器符合STL规格,又进行一次简单的封装:
template<class T, class Alloc>
class simple_alloc{
public:
static T* allocate(size_t n)
{
return 0 == n ? 0 : (T*)Alloc::allocate(n * sizeof(T));
}
static T* allocate(void)
{
return (T*)Alloc::allocate(sizeof(T));
}
static void deallocate(T *p, size_t n)
{
if(n != 0)
Alloc::deallocate(p, n * sizeof(T));
}
static coid deallocate(T *p)
{
Alloc::deallocate(p, sizeof(T));
}
}
如何使用这个接口:
template <class T, class Alloc = alloc>
class vector {
protected:
typedef simple_alloc<value_type, Alloc> data_allocator;
...
}
SGI STL 第一级配置器
template<int inst>
class __malloc_alloc_template{...};
其中:
1. allocate()直接使用malloc();
2. deallocate()直接使用free();
3. 模拟C++中的set_new_handler()以处理内存不足的情况
SGI STL 第二级配置器
template <bool threads, int inst>
class __default_alloc_template{...};
其中:
1. 维护16个自由链表,负责16种小型区块的次配置能力,内存池以malloc()配置而得。如果内存不足,转调用第一级配置器。
2. 如果需求区块大于128bytes,就调用第一级配置器