《STL源码剖析》阅读笔记之 迭代器及traits编程技法
2011-04-19 18:36
295 查看
说起STL就不能不提到迭代器,它是STL中非常关键的概念,正是它连接了容器和算法,要理解STL思想首先
要理解的便是迭代器,这篇笔记总结了我两次看《STL源码剖析》第三章的阅读笔记,其中也包含了自己的
一些想法,欢迎大家指正。
本文从三方面总结迭代器
[align=left] 迭代器的思想[/align]
[align=left] 迭代器相应型别及traits思想[/align]
[align=left] __type_traits思想[/align]
一迭代器思想
迭代器的主要思想源于迭代器模式,其定义如下:提供一种方法,使之能够依序巡防某个聚合物(容
器)所含的元素,而又无需暴露该聚合物的内部表达式。可见她的主要作用便是能够降低耦合,提高代码的
模块性。
STL的的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一贴胶着剂将它们撮合
在一起,这贴胶着剂便是迭代器。迭代器的行为类似智能指针(例如标准库的auto_ptr和boost库的shared
_ptr),换句话说它重载了*和–>运算符,由于设计一个适用于所有容器的迭代器是非常困难的,每个
迭代器都必须很了解容器,所以STL的每一种容器都提供了相应的专属迭代器。
STL在广义上有5种迭代器类型(不限于这5种,还可以是原生指针等,具体的容器定义自己的迭代器但
是类型是这几种之一或者是原生指针等)
[align=left] inputiterator:这种迭代器所指对象不允许外界改变,即是只读的[/align]
[align=left] outputiterator:唯写[/align]
[align=left] forwarditerator:允许写入型算法[/align]
[align=left] bidirectionaliterator:可双向移动的迭代器[/align]
[align=left] randomaccessiterator: 涵盖所有指针运算能力,可随机访问任何位[/align]
它们在STL中的定义如下:
[/code]
二迭代器相应型别及traits思想
书中把traits技法称为STL源代码的门钥,可见其十分重要。先介绍迭代器相应型别,从字面上意义来
说便是和迭代器相关的类型信息,实际上有5种常用的迭代器类型:
[align=left] valuetype:迭代器所指对象的型别[/align]
[align=left] differencetype:迭代器之间的距离型别[/align]
[align=left] referencetype:迭代器引用型别[/align]
[align=left] pointertype:迭代器指针型别[/align]
[align=left] iterator_category:迭代器本身的型别[/align]
STL内部需要知道当前的迭代器的这些型别信息,其所使用的方法主要是模板的参数推导、模板内嵌型
别以及模板偏特化。这里介绍下模板偏特化的概念。
模板的偏特化是指任何template参数更进一步的条件限制所设计出来的一个特化版本,例如
template<typenameT>
classC{…} //这个版本允许T为任何类型
template<typenameT>
classC<T*>{…}//这个特化版本仅适用于“T为原生指针的”的情况,它比上面的更特殊
要理解的便是迭代器,这篇笔记总结了我两次看《STL源码剖析》第三章的阅读笔记,其中也包含了自己的
一些想法,欢迎大家指正。
本文从三方面总结迭代器
[align=left] 迭代器的思想[/align]
[align=left] 迭代器相应型别及traits思想[/align]
[align=left] __type_traits思想[/align]
一迭代器思想
迭代器的主要思想源于迭代器模式,其定义如下:提供一种方法,使之能够依序巡防某个聚合物(容
器)所含的元素,而又无需暴露该聚合物的内部表达式。可见她的主要作用便是能够降低耦合,提高代码的
模块性。
STL的的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一贴胶着剂将它们撮合
在一起,这贴胶着剂便是迭代器。迭代器的行为类似智能指针(例如标准库的auto_ptr和boost库的shared
_ptr),换句话说它重载了*和–>运算符,由于设计一个适用于所有容器的迭代器是非常困难的,每个
迭代器都必须很了解容器,所以STL的每一种容器都提供了相应的专属迭代器。
STL在广义上有5种迭代器类型(不限于这5种,还可以是原生指针等,具体的容器定义自己的迭代器但
是类型是这几种之一或者是原生指针等)
[align=left] inputiterator:这种迭代器所指对象不允许外界改变,即是只读的[/align]
[align=left] outputiterator:唯写[/align]
[align=left] forwarditerator:允许写入型算法[/align]
[align=left] bidirectionaliterator:可双向移动的迭代器[/align]
[align=left] randomaccessiterator: 涵盖所有指针运算能力,可随机访问任何位[/align]
它们在STL中的定义如下:
template<classT,classDistance>structinput_iterator{typedefinput_iterator_tagiterator_category;
[/code]
typedefTvalue_type;
typedefDistancedifference_type;
typedefT*pointer;
typedefT&reference;
};
structoutput_iterator{
typedefoutput_iterator_tagiterator_category;
typedefvoidvalue_type;
typedefvoiddifference_type;
typedefvoidpointer;
typedefvoidreference;
};
template<classT,classDistance>structforward_iterator{
typedefforward_iterator_tagiterator_category;
typedefTvalue_type;
typedefDistancedifference_type;
typedefT*pointer;
typedefT&reference;
};
template<classT,classDistance>structbidirectional_iterator{
typedefbidirectional_iterator_tagiterator_category;
typedefTvalue_type;
typedefDistancedifference_type;
typedefT*pointer;
typedefT&reference;
};
template<classT,classDistance>structrandom_access_iterator{
typedefrandom_access_iterator_tagiterator_category;
typedefTvalue_type;
typedefDistancedifference_type;
typedefT*pointer;
typedefT&reference;
};
二迭代器相应型别及traits思想
书中把traits技法称为STL源代码的门钥,可见其十分重要。先介绍迭代器相应型别,从字面上意义来
说便是和迭代器相关的类型信息,实际上有5种常用的迭代器类型:
[align=left] valuetype:迭代器所指对象的型别[/align]
[align=left] differencetype:迭代器之间的距离型别[/align]
[align=left] referencetype:迭代器引用型别[/align]
[align=left] pointertype:迭代器指针型别[/align]
[align=left] iterator_category:迭代器本身的型别[/align]
STL内部需要知道当前的迭代器的这些型别信息,其所使用的方法主要是模板的参数推导、模板内嵌型
别以及模板偏特化。这里介绍下模板偏特化的概念。
模板的偏特化是指任何template参数更进一步的条件限制所设计出来的一个特化版本,例如
template<typenameT>
classC{…} //这个版本允许T为任何类型
template<typenameT>
classC<T*>{…}//这个特化版本仅适用于“T为原生指针的”的情况,它比上面的更特殊
有了模板偏特化,就可以让traits萃取出原生指针(譬如vector的迭代器就是原生指针型别的)以及指
向常量的原生指针型别而不仅仅是类类型的,而负责萃取的便是iterator_traits:
template<classIterator>structiterator_traits{
[/code]
typedeftypenameIterator::iterator_categoryiterator_category;
typedeftypenameIterator::value_typevalue_type;
typedeftypenameIterator::difference_typedifference_type;
typedeftypenameIterator::pointerpointer;
typedeftypenameIterator::referencereference;
};
如果是类类型的性别,用上面这个就可以获得其5个相应型别,当然这些型别必须都在相应iterator类
里面定义好(见第一节的5种迭代器的定义),那么如果不是上面5种而是原生指针等其他型别呢?这时候
就用到了模板偏特化:
//原生指针用这个template<classT>[/code]
structiterator_traits<T*>{
typedefrandom_access_iterator_tagiterator_category;
typedefTvalue_type;
typedefptrdiff_tdifference_type;
typedefT*pointer;
typedefT&reference;
};
//指向常量的原生指针用这个
template<classT>
structiterator_traits<constT*>{
typedefrandom_access_iterator_tagiterator_category;
typedefTvalue_type;
typedefptrdiff_tdifference_type;
typedefconstT*pointer;
typedefconstT&reference;
};
通过这个traits我们就可以获得任何一种iterator的相应型别,通过以下表达式即可:
iterator_traits<…>::…
说到这里有一个很重要是设计思想不得不提,就是通过函数重载在编译时决策正确的函数调用。这个问
题源于5种迭代器的类型,它们的巡防能力是不同的,例如randomacessiterator是巡防能力最强的,可以
在O(1)时间巡防指定位置,而这个用其他的迭代器可能需要O(n)。所以为了提高效率,我们应该用和迭代器
类型最匹配的算法函数去调用,能用randomaccessiterator的就不要用其他的。那么怎么做呢?
[align=left] 首先通过traits获得iterator_category,这样我们能够知道当前迭代器的类型。实际上iterator_category就是用来提供这种服务的。[/align]
[align=left] 在函数调用时生成相应迭代器类型的临时对象作为实参传递,编译器就会调用相应的重载函数。[/align]
为了重载函数识别,我们有对应的5种迭代器标识类:
structinput_iterator_tag{};
structoutput_iterator_tag{};
structforward_iterator_tag:publicinput_iterator_tag{};
structbidirectional_iterator_tag:publicforward_iterator_tag{};
structrandom_access_iterator_tag:publicbidirectional_iterator_tag{};
继承是为了可以使用传递调用,当不存在某种迭代器类型匹配时编译器会依据继承层次向上查找进行传递。
以distance为例:
//这里category()就是为了产生临时对象template<classInputIterator>
[/code]
inlineiterator_traits<InputIterator>::difference_type
distance(InputIteratorfirst,InputIteratorlast){
typedeftypenameiterator_traits<InputIterator>::iterator_categorycategory;
return__distance(first,last,category());
}
//inputiterator版,注意函数形参最后的类型
template<classInputIterator>
inlineiterator_traits<InputIterator>::difference_type
__distance(InputIteratorfirst,InputIteratorlast,input_iterator_tag){
iterator_traits<InputIterator>::difference_typen=0;
while(first!=last){
++first;++n;
}
returnn;
}
//randomaccessiterator版
template<classRandomAccessIterator>
inlineiterator_traits<RandomAccessIterator>::difference_type
__distance(RandomAccessIteratorfirst,RandomAccessIteratorlast,
random_access_iterator_tag){
returnlast-first;
}
三__type_traits思想
有了前面的基础,我们理解到STL是非常重视效率的,而SGISTL又在其基础上实现了一个
__type_traits,根据前面的经验我们知道它是一个萃取剂,只不过它萃取的型别是:
[align=left] 是否具备non-trivialdefaultctor?[/align]
[align=left] 是否具备non-trivialcopyctor?[/align]
[align=left] 是否具备non-trivialassignmentoperator?[/align]
[align=left] 是否具备non-trivialdtor?[/align]
这里的non-trivial意指非默认的相应函数,我们知道编译器会为每个类构造以上四种默认的函数,如
果没有定义自己的,就会用编译器默认函数,如果使用默认的函数,本来就是按位拷贝我们可以使用memcpy
等函数来加快速度,提高效率。
为了使用函数重载决议,我们使用类类型来定义两种类型,__true_type和__false_type
struct__true_type{
};
struct__false_type{
};
template<classtype>
struct__type_traits{
typedef__true_typethis_dummy_member_must_be_first;
typedef__false_typehas_trivial_default_constructor;
typedef__false_typehas_trivial_copy_constructor;
typedef__false_typehas_trivial_assignment_operator;
typedef__false_typehas_trivial_destructor;
typedef__false_typeis_POD_type;
};这个是泛化版,STL对几乎每种内置类型都定义了相应的特化版本来制定它们的类型,整体实现不难。[/code]
后记:
通过迭代器的设计,我们能够看到很多非常有价值的思想,也对模板的强大有了更加深刻的认识,这些也是
继续阅读STL源码的基础。
相关文章推荐
- 《STL源码剖析》读书笔记------第3 章 迭代器概念与traits编程技法
- 《STL源码剖析》——迭代器(iterators)概念与traits编程技法(一)
- 《STL源码剖析》——迭代器(iterators)概念与traits编程技法(二)
- STL源码剖析_读书笔记:第三章 迭代器概念与traits编程技法
- STL源码剖析 - 第3章 迭代器的概念与traits编程技法
- STL-迭代器和traits编程技法
- 迭代器(Iterator)概念与traits编程技法
- STL源码剖析 迭代器(iterator)概念与编程技法(三)
- STL源码剖析 迭代器(iterator)概念与编程技法(三)
- STL——迭代器与traits编程技法
- Ch3 迭代器概念与traits编程技法
- C++ Traits编程技法--从迭代器的设计看参数推导与类型推导
- STL(6)之Traits编程技法 与迭代器
- 【STL源码剖析读书笔记】【第3章】迭代器概念与traits编程技法
- 【STL源码剖析读书笔记】【第3章】迭代器概念与traits编程技法
- 迭代器(iterator) 与 traits 编程技法
- STL源码分析读书笔记--第三章--迭代器(iterator)概念与traits编程技法
- 《stl源码剖析》-- 迭代器概念和trait编程技法
- 迭代器概念与traits编程技法
- 3 迭代器与概念和traits编程技法