STL源码剖析(二)

迭代器

迭代器是一种行为类似指针的对象,而指针的各种行为中最常见也最重要的是内容提纲(dereference)和成员访问,因此迭代器最重要的编程工作是对operator*和operator->进行重载工作。
参考auto_ptr的代码,重点以下两行:

T& operator *()const { return *pointee;}
T*  operator->()const{ return pointee;}

为了设计迭代器会无可避免地暴露细节,因此就把迭代器的开发工作交给对应的设计者,如此一来所有实现细节反而得以封装起来不被使用者看到,这正是为什么每一种STL容器都提供专属迭代器的原因。

使用function template的参数推导机制来获得相应型别。
例如
在这里插入图片描述
但是迭代器相应型别不只是“迭代器所指对象的型别”一种而已,根据经验,最常用的相应型别有五种,然而并非任何情况下任何一种都可以利用上述的template参数推导机制来获得。

Traits编程技法

这一章节最重要的部分,同时其方法很巧妙。

迭代器所指对象的型别,称为该迭代器的value type,上述的参数型别推导推而导之的指示参数,无法推导函数的回返值型别。这时就需要另一种方法,为traits,

template<class T>
struct MyIter{
typedef T value_type;
T*ptr;
MyIter(T*p=0):ptr(p){}
//...
};

template<class I>
typename I::value_type func(I ite){ return *ite}
//...
MyIter<int>ite(new int(8);
cout<<func(ite);//输出8

在上面的代码中,func()函数的返回值是value_type,需要加上关键词typename(这是模板编程的知识,已经有了解,不重复)。

但是并不是所有的迭代器都是class type,原生指针就不是,在以上代码,如果不是class type就无法为它定义内嵌型别,但是STL以及整个泛型思维绝对必须接受原型指针作为一种迭代器,因此需要针对特定情况做特殊处理,这可以使用template partial specialization 偏特化的方式处理,偏特化的定义:针对任何template参数更进一步的条件限制所设计出来的一个特化版本。
例如

template<typename T>
class C{...};//这个版本允许接受T为任何型别

template<typename T>
class C<T*>{...};//这个特化版本仅适用于T为原生指针的情况, T为原生指针便是T为任何型别的一个更进一步的条件限制。

根据前面的讲解,就能够为迭代器设计特化版。

template <class I>
struct iterator_traits{
typedef typename I::value_type value_type;
};
//这个意思就是如果I有定义子的valuetype, 那么通过这个traits的作用,萃取出来的valuetype就是I::valuetype。
之前的代码可以改为
template<class I>
typename I::iterator_trait<I>::valuetype  func(I ite){ return *ite}
//...

注意的是,以上这种写法和之前比,就是多了一层间接性的意思,在本例子中,MyIter类还是没有改变的。在这里,除了多一层间接性,好处是traits可以拥有特化版本。
通常特化版本需要包含原生指针和指向常数对象的指针两种。

template<class T>
struct iterator_traits<T*>{
typedef T value_type;
};//原生指针虽然不是一种classtype,但是也能通过traits取其valuetype.
template<class T>
struct iterator_traits<const T*>{
typedef T value_type;
};//对于pointer-to-const,萃取出来的型别是T,而不是const T.

按照这种方法,最常用的迭代器相应型别有五种,如果希望开发的容器与STL更好的联系,就得需要。分别是value type ,defference type,pointer,reference,iterator_catagory.如下图

在这里插入图片描述
除了iterator_catagory都是需要对原生指针和指向常数的指针提供特化版本,方法和上面比较类似。需要记载的,比较要注意的是 typedef T* pointer; typedef T& reference;
iterator_catagoly,迭代器分为五类
如图在这里插入图片描述
在一个算法的实现中,可以使用多种的迭代器实现,具体需要选择哪个版本,如果在执行时期才决定使用哪个版本,会影响程序效率,最好能够在编译期选择正确的版本,重载机制可以达成这个目标,但是因为都是template参数,需要加上一个型别已经确定的函数参数,使函数重载机制得以运行起来。我们可以利用迭代器类型相应型别作为函数的额外的一个参数,该相应型别必须是一个class type,不能只是数值号码类的东西,因为编译器需要仰赖它进行重载决议。
在这里插入图片描述
在这里插入图片描述
由于只在内部使用,所以函数名称前家上特定的前导符号_,在这里以算法advance为例子,第一张图定义了五个作为标记的类型,第二张图就是对应不同的标记,_advance()函数的定义,(一开始我看错了,以为是调用,想了很久),调用的部分在后面。
在这里插入图片描述
在这里,iterator_trait::iterator_category()将产生一个暂时对象,道理就像int()产生一个int暂时对象那样,其类型为前面五个类型之一,然后根据这个限制,编译器去调用函数。
这五个类型,是通过下面这张图,定义为iterator_category的相应型别。
在这里插入图片描述
在前面的五个类中,可以发现有继承关系,通过继承关系,如果函数调用时,参数没有完全吻合,因继承关系就是自动调用所继承的那个类的函数。
STL算法的命名规则,以算法所能接收的最初级类型为其迭代器型别参数命名。(由于迭代器之间存在继承关系,传递调用的行为模式就会自然的存在)。
STL提供了一个iterators class ,如果每个新设计的迭代器都继承自它,就可以保证符合STL所需的规范。
在这里插入图片描述
总结:traits编程技巧大量运用于STL实现品中,它利用内嵌型别的编程技巧与编译器的template的参数推导功能。

__type_traits,双底线前缀词意指这是SGI STL内部所用的东西,不在STL标准规范内。
iterator_traits负责萃取迭代器的特性,__type_traits负责萃取型别(type)的特性。如果不具备关注的型别特性,就能采用最有效率的措施,例如直接内存处理操作。
在这里插入图片描述
上述的式子应该传回

struct __true_type{};
struct __false_type{};

这两个空白class没有任何成员,不会带来额外的负担,却又能表示真假。
紧接着 就是需要在 __type_traits结构中定义__flase_type或者__true_type
对于一些编译器需要提供适当的特化版本,char int、double 之类。
有了__type_traits,就可以更灵活地调用高效率的方法。
一个使用的例子
typedef typename __type_traits::is_POD_type is_POD;
再通过判断is_POD的类型是 __true_type还是__false_type,进而调用不同的函数,有点类似上面介绍中根据不同的参数调用不同的迭代器,但这是根据不同的参数,调用更有效率的方法。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值