【STL源码剖析】迭代器

STL的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一帖胶着剂将它们撮合在一起。容器和算法的泛型化,用class templates和function templates就可以分别达成目标。

迭代器是一种智能指针。


为了获得迭代器所指对象的型别,一种是利用了function template的参数推导机制。例子如下:

template <class I,class T>
void func_impl(I iter,T t)
{
    T tmp;    //T就是迭代器所指之物的型别
    //。。。完成func()应该做的全部工作。
}

template <class I>
void func(I iter)
{
    func_impl(iter,*iter);    //func的工作全部移往func_impl
}

int main()
{
    int i=100;
    func(&i);
}

我们以func()为对外接口,却把实际操作全部置于func_impl()之中,由于func_impl是一个function template,一旦被调用,编译器就会自动进行template参数推导,导出型别T,解决了问题。


Traits编程技法

上述的参数型别推导技巧虽然可以用于value type,却非全面可用:万一value type必须用于函数的传回值,就束手无策了。因为函数的template参数推导机制推导的只是参数,无法推导函数的返回值型别。

Traits技法采用内嵌型别来实现获取迭代器型别这一功能需求,其意义是,如果I定义有自己的value type,那么通过这个traits的作用,萃取出来的value_type就是I::value_type。最常用的五种迭代器型别:

template <class I>
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;
}
  • value type:迭代器所指对象的型别。
  • difference type:表示两个迭代器之间的距离。
  • reference type
  • pointer type
  • iterator_category。为下面的函数重载做准备。

根据移动特性与施行操作,迭代器被分为五类:

  • Input Iterator:只读
  • Output Iterator:唯写
  • Forward Iterator:允许“写入型”算法在此种迭代器所形成的区间上进行读写操作。
  • Bidirectional Iterator:可双向移动。
  • Random Access Iterator:前四种迭代器都只供应一部分指针算术能力,第五种则涵盖所有指针算数能力。

设计算法时,尽量针对某种迭代器提供一个明确定义,并针对更强化的某种迭代器提供另一种定义,这样才能在不同情况下提供最大的效率。


以advanced()为例

template <class InputIterator,class Distance>
void advance_II(InputIterator& i,Distance n)
{
    //单向,逐一前进
    while(n--)
        ++i;
}

template <class BidirectionalIterator,class Distance>
void advance_BI(BidirectionalIterator& i,Distance n)
{
    //双向,逐一前进
    if(n>=0)
        while(n--)
            ++i;
    else
        while(n++)
            --i;
}


template <class RandomAccessIterator,class Distance>
void advance_RAI(RandomAccessIterator& i,Distance n)
{
    //双向,跳跃前进
    i+=n;
}

在程序调用advance()时,如果选择advance_II(),对于RandomAccessIterator来说很没效率(O(1)变O(n)),如果用advance_RAI(),无法接受InputIterator,现在我们要将三者合一,如果采用:

template <class InputIterator,class Distance>
void advance(InputIterator& i,Distance n)
{
    if(is_random_access_iterator(i))
        advance_RAI(i,n);
    else if(is_bidirectional_iterator(i))
        advance_BI(i,n);
    else
        advance_II(i,n);
}

这样是在执行期间才决定使用哪一个版本,会影响效率。最好在编译期就选择了正确的版本,是不是想起了重载函数呀。

我们先定义五个class,表示五种迭代器类型,作为标记使用。

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    {};

用继承,可以不必再写"单纯只做传递调用"的函数,比如forward_iterator版本的advance(),还是调用的input_iterator版本,不用再写一个advance_FI版本。

现在我们来写重载函数__advance()(由于只在内部使用,所以函数名称加上特定的前导符)

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,Distance n>
inline void advance(InputIterator& i,Distance n)
{
    __advance(i,n,iterator_traits<InputIterator>::iterator_category());
}
//iterator_traits<InputIterator>::iterator_category()将产生一个临时对象,隶属于前述四个迭代器//类型之一,然后编译器决定如何重载。
//template <class InputIterator,Distance n>中的InputIterator是因为STL的命名规则:
//以算法所能接受之最低阶迭代器类型,来为其迭代器型别参数命名。

总结

设计适当的迭代器是容器的责任,只有容器才知道设计怎样的迭代器来遍历自己,并执行迭代器该有的各种行为。

traits编程技法大量运用于STL实现品中。它利用“内嵌型别”的编程技巧与编译器的template参数推导功能,增强C++未能提供的关于型别认证方面的能力。

SGI STL的私房菜:__type_traits,以后再更新吧,很有趣的一个技术。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值