STL源码——迭代器iterators

1.迭代器作用

迭代器(iterators)是一种抽象的设计概念,现实程序语言中并没有直接对应于这个概念的实物。《Design Patterns》中 iterator模式定义如下:提供一种方法,使之能够依序巡访某个聚合物(容器)所含的各个元素,而又无需暴露该聚合物的内部表述方式。STL的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一贴胶合剂(iterator)将它们撮合在一起。 迭代器是数据结构容器中非常有用的“特殊指针”,在不暴露容器中结构的条件下,可以取出容器里所有的元素。

2.迭代器是一种智能指针

迭代器是一种行为类似指针的对象,而指针的各种行为中最常见也最重要的便是内容提领( dereference)和成员访问( member access),因此,迭代器最重要的编程工作就是对operator* 和operator->进行重载overloading)工作。


3.不同的容器都有专属的迭代器


为了实现细节得以封装起来不被使用者看到,每一种STL容器都需要提供专属迭代器。那么问题就来了: 在算法中运用迭代器时,很可能会用到相应类型(associated type)。什么是相应类型?迭代器所指之物的类型便是其中之一。假设算法中有必要声明一个变量,以“迭代器所指对象的类型”为类型,如何处理?


3.1函数模板类型自推

为了判断迭代器的类别,需要进行参数推导。解决办法是:利用function template的参数推导( argument deducation)机制。所谓 “自推” 指的是编译器在某些情况下,可以根据调用方提供的信息来补全用户未提供的模板参数,是模板实例化 (template instantiation) 的一个步骤,发生的时机是在函数模版调用时 (invoke time of function template)。也就是说,当需要的时候,每次模版函数的调用,均会 (根据调用方提供的信息) 触发一次潜在的模板参数类型推导。

#include<iostream>
using namespace std;
/*一个功能不完备的迭代器,但用于举例已经足够了,只需要实现deference运算符重载*/
template<class Item>
class iter
{
public:
    typedef Item* pointer;
    typedef Item& reference;
public:
    iter(pointer ptr = 0)        //默认构造方法
        :_ptr(ptr){  }
    reference operator*() const { return *_ptr; }    //dereference
private:
    pointer _ptr;
};
template<class I, class T>
void func_impl(I iter, T type)
{
    T output = *iter;
    cout << "利用函数模板类型自推:";
    cout << output << endl;
}
template<class I>
inline
void func(I iter)    //func函数实际上是对外接口
{
    func_impl(iter, *iter);
}
int main()
{
    int i = 10;
    func(&i);
    return 0;
}

我们以 func ()为对外接口,却把实际操作全部置于func_.impl (〉之中。由于func_impl ( 〉是一个function template,一旦被调用,编译器会自动进行template参数推导。于是导出型别T,顺利解决了问题。
“为了与C保持兼容,返回值并非是调用函数时的必要条件,因此函数模版类型推导和函数重载都不能且不应依赖返回值。”
函数模板类型自推是有局限性的,对于上述代码,试图通过返回值类型推导出T到底是什么类型是可取的。但,如果函数返回值类型为void,参数类型列表(parameter type list)为空,那么无论怎样都无法处理,需要另辟蹊径。引出了声明内嵌类型这一手段。

3.2声明内嵌类型

#include<iostream>
using namespace std;
template<class T>
class iter
{
public:
    typedef T* pointer;
    typedef T& reference;
    typedef T value_type;    //声明内嵌类型
public:
    iter(pointer p = 0)
        :_ptr(p){ }
    reference operator *()const
    {
        return *_ptr;
    }
private:
    pointer _ptr;
};
template<class I>
typename I::value_type        //返回值类型
func(I ite)
{
    return *ite;
}
int main()
{
    int a = 10;
    iter<int> ite(&a);
    cout << func(ite) << endl;
    return 0;
}

注意,func()的回返型别必须加上关键词typename,因为T是一个template参数,在它被编译器具现化之前,编译器对T 一无所悉,换句话说,编译器此时并不知道MyIter<T> : : value_type代表的是一个型别或是一个memberfunction或是一个data member。关键词typename 的用意在于告诉编译器这是一个型别,如此才能顺利通过编译。

但并不是所有迭代器都是class type。原生指针(类型* 变量名,可以理解为一般的指针)就不是,如果不是 class type,就无法为它定义内嵌型别。但 STL(以及整个泛型思维)绝对必须接受原生指针作为一种迭代器,所以上面这样还不够。有没有办法可以让上述的一般化概念针对特定情况(例如针对原生指针)做特殊化处理呢?

3.3类型萃取机 

Partial Specializa tion(偏特化〉的意义: template partial specialization,大致的意义是:如果 class template拥有一个以上的template参数,我们可以针对其中某个(或数个,但非全部) template 参数进行特化工作。换句话说,我们可以在泛化设计中提供一个特化版本(也就是将泛化版本中的某些template 参数赋予明确的指定)。

partial specialization的字面意义容易误导我们以为,所谓“偏特化版”一定是对template参数u或v或T(或某种组合)指定某个参数值.其实不然,[Austern99]对于partial specialization的意义说得十分得体:“所谓partial specialization的意思是提供另一份template定义式,而其本身仍为templatized”。《泛型思维》一书对 partial specialization的定义是:“针对(任何) template参数更进一步的条件限制所设计出来的一个特化版本”。由此,面对以下这么一-个class template:
//我们更容易接受它有一个形如下的Partial Specializationtemplate<typename T>class C<T*> {……};  //这个泛化版本仅适用于”T为原生指针”的情况,便是”T为任何型别”的一个更进一步的条件限制。

假设有一个class template 如下:

template<typename U, typename V, typename T>
class C {……};  //这个泛化版本允许(接受)T为任何型别
//我们更容易接受它有一个形如下的Partial Specialization
template<typename T>
class C<T*> {……};  //这个泛化版本仅适用于"T为原生指针"的情况,便是"T为任何型别"的一个更进一步的条件限制。

于是,我们可以解决前述“内嵌型别”未能解决的问题。先前的问题是,原生指针并非class,因此无法为它们定义内嵌型别。现在,我们可以针对“迭代器之template参数为指针”者,设计特化版的迭代器。

template<class T>
struct miterator_traits<T*>        //1号
{
    typedef T value_type;
};
int main()
{
    miterator_traits<const int*>::value_type test1;
    test1 = 2;        //会报错!!!常量不可修改
    return 0;
}

template<class T>
struct miterator_traits<T*>        //1号
{
    typedef T value_type;
};
int main()
{
    miterator_traits<const int*>::value_type test1;
    test1 = 2;        //会报错!!!常量不可修改
    return 0;
}

在代码中出现的1号萃取机参数类型是const int*,经过萃取之后,T经过编译器确认是const int。我们需要提取出类型int(non-const),应该加上一个新版本。

template<class T>        //2号
struct miterator_traits<const T*>
{
    typedef T value_type;
};
int main()
{
    miterator_traits<const int*>::value_type test1;
    test1 = 2;
    cout << test1 << endl;
    system("pause");
    return 0;
}

此时得到正确结果:

4、迭代器分类


在STL中,原生指针也是一种迭代器,除了原生指针以外,迭代器被分为五类:

1、Input Iterator:
此迭代器不允许修改所指的对象,即是只读的。支持==、!=、++、、->等操作。
2、Output Iterator
允许算法在这种迭代器所形成的区间上进行只写操作。支持++、等操作。
3、Forward Iterator:
允许算法在这种迭代器所形成的区间上进行读写操作,但只能单向移动,每次只能移动一步。支持Input Iterator和Output Iterator的所有操作。
4、Bidirectional Iterator:
允许算法在这种迭代器所形成的区间上进行读写操作,可双向移动,每次只能移动一步。支持Forward Iterator的所有操作,并另外支持–操作。
5、Random Access Iterator:
包含指针的所有操作,可进行随机访问,随意移动指定的步数。支持前面四种Iterator的所有操作,并另外支持it + n、it – n、it += n、 it -= n、it1 – it2和it[n]等操作。

这些迭代器的分类与从属关系,可以用图3-2表示。直线与箭头代表的并非C++的继承关系,而是所谓concept(概念)与refinement(强化)的关系。

5.总结:


1、类型萃取机为什么会存在?

迭代器主要分两类:一个是类去实现(常见如iterator),另一个是以原生指针实现。
函数模板类型自推:解决了推导参数类型列表的问题,但没有完全解决其他问题,如定义返回值类型等。这个时候就出现了内嵌类型声明;
内嵌类型声明:解决了迭代器是以类实现的情况,无法解决迭代器以原生指针实现的情况。就出现了萃取机;
类型萃取机: 出现的目的主要是为了解决原生指针类型萃取所存在的问题(因为手段2无法解决),原生指针的问题解决了之后,把类去实现的迭代器也作为类型萃取机的操作对象的话,类型萃取机就能处理所有的迭代器了(类型萃取机就该做到这一点)。而需要处理类实现的迭代器相应类型的提取,只不过多了一层间接性而已。正如代码中0号萃取机所展示的那样。

2.示意图

3.总结:

设计适当的相应型别 ( associated types),是迭代器的责任。设计适当的迭代器,则是容器的责任。唯容器本身,才知道该设计出怎样的迭代器来遍历自己,并执行迭代器该有的各种行为(前进、后退、取值、取用成员…)。至于算法,完全可以独立于容器和迭代器之外自行发展,只要设计时以迭代器为对外接口就行。
traits 编程技法大量运用于STL实现品中。它利用“内嵌型别”的编程技巧与编译器的template参数推导功能,增强C++未能提供的关于型别认证方面的能力,弥补C++不为强型别( strong typed)语言的遗憾。


6.iterator源码

#ifndef _ITERATOR_H_
#define _ITERATOR_H_
#include<cstddef>


namespace EasySTL
{
    //五种迭代器,作为标记型别(tag types),不需要任何成员
    struct input_iterator_tag{};
    struct output_iteratoe_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 T,class Distance>
    struct input_iterator
    {
        typedef input_iterator_tag  iterator_category;
        typedef T                   value_type;
        typedef Distance            difference_type;
        typedef T*                  pointer;
        typedef T&                  reference;
    };


    template<class T,class Distance>
    struct output_iterator
    {
        typedef output_iterator_tag  iterator_category;
        typedef void                 value_type;
        typedef void                 difference_type;
        typedef void                 pointer;
        typedef void                 reference;
    };


    template<class T,class Distance>
    struct forward_iterator
    {
        typedef forward_iterator_tag  iterator_category;
        typedef T                     value_type;
        typedef Distance              difference_type;
        typedef T*                    pointer;
        typedef T&                    reference;
    };


    template<class T,class Distance>
    struct bidirectional_iterator
    {
        typedef bidirectional_iterator_tag  iterator_category;
        typedef T                           value_type;
        typedef Distance                    difference_type;
        typedef T*                          pointer;
        typedef T&                          reference;
    };


    template<class T,class Distance>
    struct random_access_iterator
    {
        typedef random_access_iterator_tag  iterator_category;
        typedef T                           value_type;
        typedef Distance                    difference_type;
        typedef T*                          pointer;
        typedef T&                          reference;
    };


    //为避免写代码时挂一漏万,自行开发的迭代器最好继承自下面这个 std::iterator
    template<class Category,class T,class Distance=ptrdiff_t,class Pointer=T*,class Reference=T&>
    struct iterator
    {
        typedef Category  iterator_category;
        typedef T         value_type;
        typedef Distance  difference_type;
        typedef Pointer   pointer;
        typedef Reference reference;
    }


    //"榨汁机"traits
    template<class Iterator>
    struct iterator_traits
    {
        typedef typename Iterator::iterator_category  iterator_category;
        typedef typename Iterator::value_type         value_type;
        typedef typename Iterator::difference_type    difference_type;
        typedef typename Iterator::pointer            pointer;
        typedef typename Iterator::reference          reference;
    }


    //针对原生指针(naive pointer)而设计的traits偏特性化版本
    template<class T>
    struct iterator_traits<T*>
    {
        typedef random_access_iterator_tag  iterator_category;
        typedef T                           value_type;
        typedef ptrdiff_t                   difference_type;
        typedef T*                          pointer;
        typedef T&                          reference;
    };


    //针对Pointer-to-const而设计的traits偏特化版本
    template<class T>
    struct iterator_traits<const T*>
    {
        typedef random_access_iterator_tag  iterator_category;
        typedef T                           value_type;
        typedef ptrdiff_t                   difference_type;
        typedef const T*                    pointer;
        typedef const T                     reference;
    };


    //判断某iterator的类型
    template<class Iterator>
    inline typename iterator_traits<Iterator>::iterator_category
    iterator_category(const Iterator& It)
    {
        typedef typename iterator_traits<Iterator>::iterator_category
        return category();
    }


    //获取迭代器的value_type
    template<class Iterator>
    inline typename iterator_traits<Iterator>::value_type*
    value_type(const Iterator& It)
    {
        return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
    }


    //获取迭代器的distance_type
    template<class Iterator>
    inline typename iterator_traits<Iterator>::difference_type*
    distance_type(const Iterator& It)
    {
        return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
    }


//....................................................................


/*函数重载,使迭代器能在编译时期就确定调用哪个函数*/  
template <class _InputIter, class _Distance>  
/*迭代器类型为input_iterator_tag的函数定义*/  
inline void __advance(_InputIter& __i, _Distance __n, input_iterator_tag) {  
  while (__n--) ++__i;  
}  




template <class _BidirectionalIterator, class _Distance>  
/*迭代器类型为bidirectional_iterator_tag的函数定义*/  
inline void __advance(_BidirectionalIterator& __i, _Distance __n,   
                      bidirectional_iterator_tag) {  
  __STL_REQUIRES(_BidirectionalIterator, _BidirectionalIterator);  
  if (__n >= 0)  
    while (__n--) ++__i;  
  else  
    while (__n++) --__i;  
}  




template <class _RandomAccessIterator, class _Distance>  
/*迭代器类型为random_access_iterator_tag的函数定义*/  
inline void __advance(_RandomAccessIterator& __i, _Distance __n,   
                      random_access_iterator_tag) {  
  __STL_REQUIRES(_RandomAccessIterator, _RandomAccessIterator);  
  __i += __n;  
}  


template <class _InputIterator, class _Distance>  
/*决定调用哪个函数,这是一个对外接口*/  
inline void advance(_InputIterator& __i, _Distance __n) {  
  __STL_REQUIRES(_InputIterator, _InputIterator);  
  __advance(__i, __n, iterator_category(__i));  
}




//....................................................................
   template<class InputIterator>
    inline typename std::iterator_traits<InputIterator>::difference_type distance(InputIterator first, InputIterator last){
        return __distance(first, last, std::iterator_traits<InputIterator>::iterator_category());
    }
    template<class InputIterator>
    inline typename std::iterator_traits<InputIterator>::difference_type= 0;
            while (first != last){
                ++first; ++n;
            }
            return n;
        }


    template<class InputIterator>
    inline typename std::iterator_traits<InputIterator>::difference_type \
        __distance(InputIterator first, InputIterator last, std::random_access_iterator_tag){
            return last - first;
        }


//....................................................................
}


#endif

7.参考文献:

侯捷《STL源码剖析》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值