stl源码剖析读书笔记之allocator

    此系列是本人阅读《STL源码剖析》的一点心得体会,并摘录了部分STL源码加深理解。此篇博客为第二章allocator的读书笔记。

1.construct()和destroy()是全局函数

    第二章开始作者定义了一个简单的模板类 JJ::allocator ,有allocator的所有接口,包括 allocator() deallocate() construct() destroy() 四个成员函数 。但是打开 STL源码中的 stl_alloc.h ,SGI STL用的最多的std::alloc 并没有construct() destroy() 成员函数,SGI STL在这一点上没有遵守STL规范。construct() 和 destroy()被声明为全局函数。

2.destroy()

    construct() 和 destroy() 全局函数定义于stl_construct.h 。construct()调用new操作符,没什么好说的,这里主要说说destroy()。
    destroy()可以接受一个指针作为参数,调用相应的析构函数,如下所示。

                template <class _Tp>
                inline void destroy(_Tp* __pointer) {
                  _Destroy(__pointer);
                }
                template <class _Tp>
                inline void _Destroy(_Tp* __pointer) {
                  __pointer->~_Tp();
                }

    这是泛化的函数模板,如果是基本类型的指针,显然无法调用析构函数,书上说有基本类型指针的特化函数模板,但实际上源码里是非模板函数,由于非模板函数优先级高于模板函数,遇到类似int*的指针编译器会调用非模板函数,并且不做任何操作,如下所示。

inline void _Destroy(char*, char*) {}
inline void _Destroy(int*, int*) {}
inline void _Destroy(long*, long*) {}
inline void _Destroy(float*, float*) {}
inline void _Destroy(double*, double*) {}


    destroy()还可以接受两个iterator参数,来析构一个区间的所有对象。这就有一个问题,如果这个区间很大,而每个对象的析构函数都是无关紧要的(即trivial destructor,析构函数里什么都不需要做),那么一个个调用析构函数很影响效率。所以需要判断,此对象的析构函数是否是non-trivial。

            template <class _ForwardIterator>
            inline void destroy(_ForwardIterator __first, _ForwardIterator __last) {
              _Destroy(__first, __last);
            }
            template <class _ForwardIterator>
            inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) {
              __destroy(__first, __last, __VALUE_TYPE(__first));
            }

            /*****************萃取__type_traits::has_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());
            }

            /*******************如果no-trivial destructor 依次destroy***********************/
            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) {}

    因为要在编译期就决定是一个个调用析构函数还是什么都不做,所以不可能用if…else…语法(运行期才去执行),而是依靠template参数推导。如上代码,有两个函数模板__destroy_aux() ,最后一个参数是 _false_type 还是 __true__type决定了destroy()的操作内容,而这个参数又是从type_traits中萃取出来的,在第3点我们就来讲讲这个type_traits。
    这实际上就是泛型编程的常用技巧了,编译期完成的工作不要到运行期再去做,而是依靠template参数推导等技巧来完成类似于if…else…的判断。

3.type_traits

    这一部分放在了第三章iterator,在讲完了可以萃取特性的iterator_traits后顺带引出可以萃取更多特性的type_traits,更容易让人理解。不过要在第二章allocator碰到type_traits就要黑人问号了。所以在这里说明一下。
    type_traits就是告诉编译器以下信息,这个型别是否一定要执行默认构造函数(has_trivial_default_constructor),是否一定要执行拷贝构造函数( has_trivial_copy_constructor),是否一定要执行赋值运算符(has_trivial_assignment_operator),是否一定要执行析构函数(has_trivial_destructor),是不是POD类型(is_POD_type)。举一个char特化模板的例子如下。

struct __true_type {
};
struct __false_type {
};

__STL_TEMPLATE_NULL struct __type_traits<char> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

    本来我也不太理解为什么要用两个struct来表示__false_type__true_type ,直到又去回味destroy()源码才明白,仔细看以下两行。

 typedef typename __type_traits<_Tp>::has_trivial_destructor
                      _Trivial_destructor;
__destroy_aux(__first, __last, _Trivial_destructor());

    因为是要用template参数推导来确定特化哪一个__destroy_aux(),所以就需要两个class object来让编译器进行参数推导,于是定义了两个空的struct。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值