C++的class templates和function templates可以实现容器和算法的泛型化。难点和关键是设计这两者的胶着剂角色——迭代器——提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴漏该容器的内部数据结构和内部表述方式。
迭代器是一种Samart pointer。
每一种STL容器都提供有专属的迭代器!原因:为了让实现细节封装起来而不让使用者看到,所以把迭代器的开发工作交给了具体容器的设计者。
泛型指针,原生指针和智能指针:
1. 泛型指针 泛型指针有多种含义。 (1) 指void*指针,可以指向任意数据类型,因此具有“泛型”含义。 (2) 指具有指针特性的泛型数据结构,包含泛型的迭代器、智能指针等。 广义的迭代器是一种不透明指针,能够实现遍历访问操作。通常所说的迭代器是指狭义的迭代器,即基于C++的STL中基于泛型的iterator_traits实现的类的实例。 总体来说,泛型指针和迭代器是两个不同的概念,其中的交集则是通常提到的迭代器类。
2. 原生指针就是普通指针,与它相对的是使用起来行为上象指针,但却不是指针。 说“原生”是指“最简朴最基本的那一种”。因为现在很多东西都抽象化理论化了,所以“以前的那种最简朴最基本的指针”只是一个抽象概念(比如iterator)的表现形式之一。 原生指针即 (类型名*p)样子的指针,类型名可以是基础类型,如int,double等,也可以是一个自己定义的Class类,相反的如果一个类重载了‘*’和‘->’的运算符,可以像指针一样用‘*’和‘->’操作,就不是原生的,如iterator等。auto_ptr(在头文件memory),这是一个用来包装原生指针的对象。
3. 智能指针是C++里面的概念:由于 C++ 语言没有自动内存回收机制,程序员每次得自己处理内存相关问题,但用智能指针便可以有效缓解这类问题。 引入智能指针可以防止出现悬垂指针的情况 一般是把指针封装到一个称之为智能指针类中,这个类中另外还封装了一个使用计数器,对指针的复制等操作将导致该计数器的值加1,对指针的delete操作则会减1,值为0时,指针为NULL。
Traits编程技法(特性萃取机)——解决迭代器常用的五种相应型别:
0、首先,我们来分析一下为什么要引入所谓的Traits。(不断加入限制条件(特例),繁衍traits的由来)
一个容器可以装各种不同类型的数据,结合value type,也就是说指迭代器所指对象的型别是各种各样的。为了解决获取“迭代器所指对象的型别”,比如“以“迭代器所指对象的型别”为型别 来 声明一个变量”,我们首先会想到function template的参数推导机制。p85——作函数参数。
我们知道,“template参数推导机制”推而导之的知识参数类型,无法推导函数的返回值型别。所以,万一,value type必须用于函数的传回值,“template参数推导机制”就行不通了。为了解决这个问题,声明内嵌型别似乎是一个好主意。p86——做函数返回值.
但是,并不是所有迭代器都是class type!原生指针就不是!如果不是原生指针,就无法定义内嵌型别。、?????STL以及整个泛型思维绝对必须接受原生指针作为一种迭代器。。。?????????。所以,为了让一般化概念针对特定情况做特殊化处理,我们使用——template partial specialization——在泛化设计中提供一个特化版本,也就是将泛化版本中某些template参数赋予明确的指定,提供另外一份template定义式,而其本身仍旧是templatized。
template <typename T>
class C {......} //这个泛化版本允许接受T为任何型别
template <typename T>
class C {......} //这个特化版本仅适用于“T为原生指针”的情况。,解决了内嵌型别未能解决的问题。
Traits,其实就是内嵌型别方法的一种升级——Traits特性萃取机,约定:自行 以内嵌型别定义的方式 定义出相应型别。
template <class T> struct iterator_traits { typedef typename I::iterator_category iterator_category; typedef typename I::value_type value_type; typedef typename I::difference_type difference_type; typedef typename I::pointer pointer; typedef typename I::reference reference; }iterator_traits必须针对传入之型别为 pointer 以及 pointer-to-const 者,设计特化版本。各相应型别的具体设计详见下面。
Traits的意义是:如果 I 定义有自己的value type,那么通过traits 的作用,萃取出来的就是I::value type。不论面对的是迭代器MyIterator,或者是原生指针int * ,或者是const Int *,我们都可以通过Traits取出正确的value type。
Traits编程技法——利用“内嵌型别”的编程技巧与编译器的template参数推导功能,增强C++未能提供的关于型别认证方面的能力,弥补C++不是强型别语言的遗憾。
1、迭代器相应型别之一:value type——指迭代器所指对象的型别
在类中定义自己的value type内嵌型别。
2、迭代器相应型别之二:difference type——用来表示两个迭代器之间的距离
template < class I ,class T> typename iterator_traits<T>::difference_type const(I first, I last, const T& value) { typename iterator_traits<I>::difference_type n = 0; for (; first != last; ++first) { if (*first == value) ++n; } return n; } template <class I> struct iterator_traits{ typedef typename I::difference_type difference_type; }; template <class I> struct iterator_traits<T*>{ //针对原生指针而设计的“偏特化”版本 typedef ptrdiff_t difference_type; }; template <class I> struct iterator_traits<const T*>{ //针对原生的pointer-to-const 而设计的“偏特化”版本 typedef ptrdiff_t difference_type; };有了上面的设计,很显然我们就可以在需要任何迭代器I的difference type的时候,统一这么写:
3、迭代器相应型别之三:reference type;迭代器相应型别之四:pointer type
Item& operator*() const { return *ptr; } Item* operator->() const { return ptr; }Item&是reference type,Item*是pointer type。“传回一个左值,令它代表p所指之物”是可以的,“传回一个地址,令它代表所指之物的地址”也是可以的。
4、迭代器相应型别之五:iterator_category——迭代器类别
iterator_category表示迭代器的分类,共有五类。input_iterator、output_iterator、forward_iterator、bidirectional_iterator、random_access_iterator,分别是只读、只写、前向移动读写、双向移动读写、随机读写。
此处,为了消除“单纯传递调用的函数”,STL运用了C++的重载技术,比如advance函数。先来看一下五种迭代器类型的从属关系:
//迭代器各类间的关系 struct input_iterator_tag {}; struct output_iterator_tag {}; struct forward_iterator_tag : public input_iterator_tag {}; struct bidirectional_iterator_tag : public forward_iterator_tag {}; struct random_access_iterator_tag : public bidirectional_iterator_tag {};如果没有上面的继承机制,我们就需要像下面一样,去传递一个调用函数:
template <class InputIterator, class Distance> inline void _advance(InputIterator& i, Distance n, input_iterator_tag) //第一个函数——单向,逐一前进 { while (n--) ++i; } template <class ForwardIterator, class Distance> inline void _advance(ForwardIterator& i, Distance n, forward_iterator_tag) { _advance(i, n, input_iterator_tag); //单纯的进行传递调用 }我们再来看使用继承机制后的调用情况:
template <class InputIterator, class Distance> inline void _advance(InputIterator& i, Distance n, input_iterator_tag) //第一个函数——单向,逐一前进 { while (n--) ++i; } template <class BidirectionalIterator, class Distance> inline void _advance(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag) //第二个函数——双向,逐一前进 { if (n >= 0) while (n--) ++i; else while (n++) --i; } template <class RandomAccessIterator, class Distance> inline void _advance(RandomAccessIterator& i, Distance n, random_access_iterator_tag) //第三个函数——双向,跳跃前进 { i += n; }
template <class InputIterator, class Distance> //控制接口——继承的使用 inline void advance(InputIterator& i, Distance n) { <span style="white-space:pre"> </span>_advance(i, n, iterator_traits(InputIterator)::iterator_category()); //产生一个暂时对象(就像int()会产生一个int暂时对象),具体调用哪一个待决定 }《STL源码剖析》源代码下载: http://pan.baidu.com/s/1c0x2kP6