您的位置:首页 > 其它

STL笔记(4)——空间配置器Allocator(二)

2016-03-04 23:11 337 查看

空间配置器Allocator(二)

空间配置器本质上是一个类模板,但是里面的设计方式就大有讲究,慢慢研究十分有意思。

两段式操作

class Foo{
};
//...
Foo *foo = new Foo;
delete foo;


new 干了两件事,一是配置内存,二是Foo::Foo()构造对象内容。

相对应的delete,也是调用析构函数将对象析构,再释放掉内存。

内存的操作和对象的构造与析构是一个两段式操作,只是new和delete帮我们打包好了服务。

在SGI的空间配置器也是按照这种两段式的方式,这里指的是
std::alloc
,而非
std::allocator
(书中已经明确说明不建议我们使用后者)。

下面分别介绍构造和分配内存,分别位于
stl_construct.h
stl_alloc.h
中。

stl_construct.h

负责对象的构造和析构,简单的来说主要是两个基本工具(函数):

construct()

destroy()

注释部分源码如下:

//stl_construct.h部分
template <class _T1, class _T2>
inline void _Construct(_T1* __p, const _T2& __value) {
new ((void*) __p) _T1(__value);//指定调用_T1的构造函数_T1::_T1(__value)来初始化指针__p所指向的内存 (placement new)
}

template <class _T1>
inline void _Construct(_T1* __p) {
new ((void*) __p) _T1();//与上述类似,默认构造函数
}


对象的构造部分比较简单,主要是用到了
placement new
这个用法。

destroy部分就有有文章了,这里先给出带注释的源码节选:

/*stl_construct.h部分*/

//destroy()第一个版本,接受一个指针
template <class _Tp>
inline void _Destroy(_Tp* __pointer) {
__pointer->~_Tp();//调用__pointer->~_Tp();
}

//如果有non-trivial destructor
template <class _ForwardIterator>
void
__destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type)
{
for ( ; __first != __last; ++__first)
destroy(&*__first);
}

//如果有 trivial destructor
template <class _ForwardIterator>
inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {}

//判断是否有trivial destructor
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());
}

//destroy的第二种形态(重载版本)
//找出迭代器所指向元素的(value type)数值型别,利用__type_traits<>求取适当的措施。
template <class _ForwardIterator>
inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) {
__destroy(__first, __last, __VALUE_TYPE(__first));
}


注意这里的destroy有两个形态,第一个形态就是直接调用析构函数,第二个形态就得一步一步讲了:

首先一个概念是
trivial destructor
&&
non-trivial destructor
这两者的区别是:

trivial destructor: 没有必要使用析构函数(例如数值类型
int
char
等)

non-trivial destructor:定义特定的析构函数。

那么对于两个迭代器确定一个范围内的对象进行析构的话,如果这些对象都是数值类型,析构函数无关痛痒,那么调用析构函数就大大影响了效率,代码中的做法是:

1. 使用
__VALUE_TYPE(__first)
找出迭代器所指向对象的类型。

2. 再利用
__type_traits<_Tp>::has_trivial_destructor
判断该类型的析构函数是否无关痛痒(trivial)

3. 再根据第2步的返回值最终使用针对这两种情况的析构函数。

stl_alloc.h

内存的分配,C++内存配置基本操作时
::operator new()
,内存释放的基本操作时
::operator delete()
。这两个全局函数相当于C中的
malloc()
free()


这个讲起来就多了什么第一级配置器和第二级配置器(这个后面慢慢看了再写),目前简单的理解了宏观的设计模式。

首先不论是第一级配置器还是第二级配置器,我们都称之为
alloc


而SGI为这个alloc还进行一个包装,部分源码如下:

/*stl_alloc.h*/
template<class _Tp, class _Alloc>
class simple_alloc {
public:
//单纯的转调用
static _Tp* allocate(size_t __n)
{ return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp)); }
static _Tp* allocate(void)
{ return (_Tp*) _Alloc::allocate(sizeof (_Tp)); }
static void deallocate(_Tp* __p, size_t __n)
{ if (0 != __n) _Alloc::deallocate(__p, __n * sizeof (_Tp)); }
static void deallocate(_Tp* __p)
{ _Alloc::deallocate(__p, sizeof (_Tp)); }
};


我们真正的容器是如何“接入”这个
alloc
呢?书中给出了一个示例:

template <class T,class Alloc=alloc>
class vector{
protected:
typedef simple_alloc<value_type,Alloc> data_allocator;
};


首先SGI空间配置器alloc的作为vector默认模板参数“赋值”给Alloc,

在vector中,Alloc作为模板形参又传给了simple_alloc作为其模板实参,最后在simple_alloc中完成“转发”
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: