STL源码剖析——P51关于空间配置器的destroy实现

在SGI源码中,关于空间的destroy功能,分别实现了两个版本,其中一个是接受一个指针的版本,

源码位于stl_construct.h中,接口及实现如下:

template< class T > inline void destroy( T* pointer ) {    pointer->~T( ); }

另一个版本接受两个可双向移动的迭代器类型的参数,接口及实现如下:

template< class ForwardIterator >

inline void destroy( ForwardIterator first, ForwardIterator last ) {

    __destroy( first, last, value_type( first ) );

    //value_type( first ) 获取first迭代器所指向的类型的指针类型,即返回T*类型的数据

    //其作用,仅仅只是一个strawman,因为指针的值是0。此处的作用只是为了通过模板参数

    //演绎,从而获取其实际类型,采用指针,只是为了节省不必要的空间消耗(指针类型所

   //占空间是固定的,但是如果以实际类型进行传值,则类型所占空间可大可小,难以把握。

   //同样,传引用的话,虽说传过去的也是地址,但引用必须有所指,换而言之,有引用变

   //量,必有相应类型占用着实际空间。所以采用值为0的指针进行传递是极佳的策略。

}

template< class ForwardIterator, class T >

inline void __destroy( ForwardIterator first, ForwardIterator last, T* ) {

    //判断T类型的析构函数是否为trivial,通过重载函数进行基于类型的条件派发

    typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor ;

    __destroy_aux( first, last, trivial_destructor );

}

template< class ForwardIterator >

inline void __destroy_aux( ForwardIterator first, ForwardIterator last, __true_type ) { }

template< class ForwardIterator >

inline void __destroy_aux( ForwardIterator first, ForwardIterator last, __false_type ) {

   for( ; first != last ; first++ ) destroy( &*first ) ;

}

===================================================================== 基于以上对源码的分析可知,对于带两个迭代器参数的destroy版本,是通过对迭代器类型的析构函数进行判断,若其析构为trivial(不可用的,无效的),则无须进行析构函数的调用,否则,循环遍历[first, last) ,逐一调用析构函数,在C++中,对于POD类型的数据,其destructor都是 trivial,有关destructor是否为trivial,可参考侯捷翻译的《深入探索C++对象模型》中有关析构语义学的内容。

通过以上分析,现在有一个正确的结论:

**结论1、如果迭代器ForwardIterator是基本类型的指针类型,如int*,那么调用destroy(first, last) 时,将转而调用__destroy_aux的空函数版本,即第三个参数是__true_type(int是POD类型)。

然而在阅读这部分内容时,我萌生了这样几个疑问:

1、如果以int*调用destroy( T* pointer )版本,则最终将执行pointer->~T() ,但这是非法的, 所以实际上会不会执行呢?

2、通过平常对STL的应用,可以断定采用int*调用destroy的两个版本,都是可以正确执行的,那么 pointer->~T()这样的语句为何没问题呢,因为经测试,在程序中写出pointer->~int()这样的语句 是不合语法规范,无法通过编译的。再者,对于destroy(first, last)版本而言,当迭代器为int* 时,调用__destroy_aux的空函数版本,它节省的效率到底体现在哪。

===================================================================== 以下是我在阅读的过程中,基于这些疑问的理解,纯属个人想法,但是这些都是经过自己的测试跟验证的。

对于第1个疑问,通过对STL源码进行调试,可以发现,当以基本类型的指针类型调用destroy(pointer) 时,pointer->~T()是不会执行的,个人觉得在进行template instantiation时,编译器对基本类型进 行了特殊处理:

如果是基本类型,则pointer->~T()语句不会生成相应的代码。(姑且这样猜测,因为我没有找到相关的资料来证实)

所以在测试 destroy( pointer )时,是跳过pointer->~T()语句所在的位置,不予执行。 于是乎,可以这样认为:

**结论2、以基本类型的指针类型调用destroy(pointer)时,destroy(pointer)实际上是一个空函数。

那么,对于疑问2,通过结论1和2可知当迭代器指向基本类型时,假设destroy(first, last)调用了 __destroy_aux的非空函数版本,则最终将遍历[first, last),并逐一为每个迭代器调用 destroy(pointer),但此时的destroy(pointer)是空函数(基于以上的猜测,所以认为是空函数),所以实现上可以认为此时

for(; first != last ; first++) destroy(first); 与 for(; first != last ; first++ ); 是等价的。(因为destroy是内联版本,所以此处也不用考虑函数调用的开销,故而是等价的。 )

故而,很显然__destroy_aux的非空函数版本和空函数版本的性能差异,在于对[first, last)的遍历, 不存在destroy(first)函数的调用及其内部执行的开销。

此处的关键在于,容易误解为: destroy(first)是不管在什么情况下都会执行的,实际上对于指向基本类型的迭代器,这个函数什么内容也没有,不过是个鸡肋。

以上为个人阅读书籍时的个人理解,不正确之处,还请高手指正。

posted @ 2012-12-17 13:35  crazylhf  阅读(303)  评论(0编辑  收藏  举报