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

空间配置器Allocator(二)

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

两段式操作

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

new 干了两件事,一是配置内存,二是Foo::Foo()构造对象内容。
相对应的delete,也是调用析构函数将对象析构,再释放掉内存。
内存的操作和对象的构造与析构是一个两段式操作,只是new和delete帮我们打包好了服务。

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

下面分别介绍构造和分配内存,分别位于stl_construct.hstl_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中完成“转发”

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值