迭代器学习笔记

来源:互联网 发布:chrome浏览器 知乎 编辑:程序博客网 时间:2024/05/16 16:58

本文是学习《STL源码剖析》时的学习笔记。

1、迭代器是一种smart pointer

迭代器是一种类,其包装了原生指针,并重载了operator*、operator->、operator!=、operator++等等,因此其行为类似指针。
容器为了储存各种类型的元素,定义为类模板。与此相似,为了使迭代器能够指向不同类型的容器元素,迭代器也需要定义为类模板。
不同的容器遍历方法不同,在重载operator++时,必须根据不同的容器确定。因此,实现迭代器时需要对容器的实现细节有非常丰富的了解。所以容器的设计者需要实现其相应的迭代器。这正是为什么每一种STL容器都提供了专属迭代器的缘故。

2、迭代器相应型别(associated type)

迭代器相应型别就是迭代器相关的一些类型信息。常用的迭代器型别有五种:value type、difference type、reference type、pointer type、iterator category;
value type:指迭代器所指对象的类型。
difference type
reference type:如果函数要传回左值,则返回值类型必须是引用。T&和const T&就是两种不同的引用类型。
pointer type:顾名思义
iterator category
如果希望自己开发的容器能与STL很好地结合使用,必须为你的容器迭代器定义这五种相应型别。

2.1为什么需要迭代器相应型别?

STL通过迭代器的使用将数据容器和算法分离开来。算法使用迭代器而不是容器作为输入。在算法中运用迭代器时,很可能会遇到其相应型别!
例如:一个函数以迭代器为输入,在函数体内部需要定义以“迭代器所指对象的类型”为类型的临时变量,这个时候就用到了value type。再例如,一个函数以迭代器为输入,需要返回迭代器所指对象的引用,这时候函数返回值类型就用到了reference type。

2.2 如何定义迭代器相应型别?

现在我们知道了迭代器相应型别很有用,那么该如何定义它呢?
使用内嵌型别声明是个不错的解决方案,看下面的例子。

    template<class T>    struct MyIter{    typedef T value_type //内嵌型别声明(nested type)    typedef T& reference_type    T* ptr;    ...    }

在迭代器MyIter中声明了value_type和reference_type,注意,它们只是在类作用域中可见。

    template<typename I>    typename I::value_type //这一行是函数的返回值,typename表明后面是一个类型名而不是变量    func1(I ite)    {return *ite;}    template<typename I>    typename I::reference_type //这一行是函数的返回值,typename表明后面是一个类型名而不是变量    func2(I ite)    {return *ite;}

上面func1返回迭代器所指对象的值,func2返回迭代器所指对象的引用。看上去这两个函数可以处理任意的迭代器(只要它声明了相应型别)。但现在出现了一个新的问题:并不是所有的迭代器都是类类型。比如说原生指针就不是,那么就无法为它定义内嵌型别!

3 Traits编程技法

为了解决上面的问题,我们需要加一个“间接层”。

具体做法是编写一个类模板,专门用来“萃取”迭代器的特性(相应型别)。

也就是说,使用一个模板类来提取迭代器相应型别。
也就是说可以像下面这样:

    template<class I>    struct iterator_traits{    typedef typename I::value_type value_type;//其实这里还是没有解决原生指针的问题    }

这个所谓的traits,其意义是,如果I定义有自己的value_type,那么通过这个traits的作用,萃取出来的value_type就是I::value_type。

原来的func1函数可以如下定义:

    template<typename I>    typename iterator_traits<I>::value_type //这一行是函数的返回值    func1(I ite)    {return *ite;}

这里的iterator_traits还是没有解决原生指针的问题。但是我们可以(必须)为原生指针T*和const T*编写一个iterator_traits特例版本,相关技术参考template partial specialization,C++primer16.5小节 模板特例化。

template<class T>struct iterator_traits<T*>{typedef T value_type;}template<class T>struct iterator_traits<const T*>{typedef T value_type;}

至此我们成功地解决了获取原生指针的相应型别的问题!
再次提醒,iterator_traits必须针对传入的型别为pointer或者point-to-const设计特化版本。