C++ 中的迭代器(Iterator)与 traits

算法与迭代器

在C++的STL中,我们最经常使用的只有两部分,一是容器(container),如vector、list、set、map等;二是各种算法,如swap、sort等。前者是模板类,后者是模板函数,也就是说,STL 中提供的算法对它们要操纵的数据一无所知,它所需要的一切信息都必须由Iterator取得,这样算法才能正常运行。

下面是find()函数的源代码

template <class _InputIter, class _Tp>
inline _InputIter find(_InputIter __first, _InputIter __last,
                       const _Tp& __val,
                       input_iterator_tag)//需要3个迭代器,前两个确定搜寻区间,后一个确定搜寻目标
{
  while (__first != __last && *__first != __val)
    ++__first;
  return __first;
}

事实上基本所有的泛型算法的都和find一样,前两个参数都是一对迭代器,通常称为first 和 last,用以表示该算法的操作区间。STL习惯采用前闭后开区间,即[first,last)。另外,在这个区间上,++操作必须能够运用,否则在运行时会导致不可预期的结果(原因之后会讲)。

迭代器是对指针的封装,所以我们暂时可以将其简单理解为指针。一共有5种类型,它们的关系如下:

  • random_access_iterator:涵盖所有指针的运算能力,对应vector等容器;
  • bidirectional_iterator:只能双向移动,即只能执行 +、- 操作,对应list(即双向链表)等容器;
  • forward_iterator:只能单向移动,即只能执行+ 操作,对应farward_list(即单向链表)等容器;
  • input_iterator:可理解为常量指针,即这种迭代器所指的对象不允许外界改变,是只读的;
  • output_iterator:只写。

前三种都由容器提供,后两种是适配器。

由上面的图可以看出,input_iterator是除了output_iterator之外的所有父类,所以find函数中的前两个参数可以传入除了output_iterator之外的任何容器的迭代器。

下面通过stl中advance函数来展现迭代器对算法的影响,advance函数的实现如下

template <class _InputIterator, class _Distance>
inline void advance(_InputIterator& __i, _Distance __n) {
  __advance(__i, __n, iterator_category(__i));
}

可以看到advance会调用__advance函数,而这个函数的实现有多个版本。如下:

template <class _InputIter, class _Distance>
inline void __advance(_InputIter& __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;
}

其他函数也大多如此,这样stl 中的算法才能为基本数据类型和种类繁多的容器服务。

迭代器与Traits

迭代器是一种包装了原生指针的对象,而指针最重要工作就是由 * 和 -> 两个操作符完成的,所以迭代器必须对这两个操作符进行重载

另外,迭代器要向算法提供容器的信息,比如 容器类型。前面提到,容器会提供迭代器供算法使用,但stl中有多种容器,但容器所能提供的迭代器却只有3种,这就意味着,我们不能可能不能通过迭代器使用到相应型别(比如如果我们要声明或返回某一中数据类型的变量,这种数据类型是不确定的)。变量声明其实还算简单,可以利用模板的参数推导机制,例如:

template<class T>
void func(T t){

...    //这里编译器会自动进行参数推导,此例中T为int类型

}

int main(){
int i;
func(i);
}

但是迭代器的相应型别不只是迭代器所指的对象的类型,最常用的相应型别有5种,所指对象的类型被称为 value type,但并不是每一种都可以用模板的参数推导来取得,另外参数推导也不能用于函数的返回值,我们需要其他方法,比如内嵌型别。其实就是

template <class T>
struct A{
    typedef T value_type;
    ...
};

这是一个泛化版本,针对原生指针和常量指针有特化版本如下

template <class T>
struct A<T*>{//针对原生指针的特化版本
    typedef T value_type;
    ...
};

template <class T>
struct A<const T*>{//针对常量指针的特化版本
    typedef T value_type;
    ...
};

特化的定义是:针对任何template参数更进一步的条件限制所设计出来的一个特化版本。可以看出来,上面的模板类专门用来萃取迭代器的特性,它的名字是Traits。实际上除了value_type之外,traits还有多种型别要“萃取”,最常用的为以下几种:

  • value_type
  • difference_type :表示两个迭代器之间的距离,也可以用来表示一个容器的最大容量
  • reference_type:T&,
  • pointer_type:T*,
  • iterator_category:迭代器的分类,就是前面提到的5种。

input_iterator 及 input_traits的实现如下

template <class _Tp, class _Distance> struct input_iterator {
  typedef input_iterator_tag iterator_category;
  typedef _Tp                value_type;
  typedef _Distance          difference_type;
  typedef _Tp*               pointer;
  typedef _Tp&               reference;
};

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

 当我们想要自己实现一个iterator的时候,可以继承stl 中的iterator 类,它的代码为      

template <class _Category, class _Tp, class _Distance = ptrdiff_t,
          class _Pointer = _Tp*, class _Reference = _Tp&>
struct iterator {
  typedef _Category  iterator_category;
  typedef _Tp        value_type;
  typedef _Distance  difference_type;
  typedef _Pointer   pointer;
  typedef _Reference reference;
};

它不含任何数据成员,纯粹只是型别定义,所以继承它没有任何负担。比如要实现一个ListIterator

template <class T>
struct ListIterator: public:std::iterator<std::forward_iterator_tag,Item>
{


...

};

SGI STL 中的__type_traits

iterator_traits负责萃取迭代器的特性,而__type__traits负责萃取type 的其他特性,这些特性有:

  • has_trivial_default_constructor
  • has_trivial_copy_constructor;
  • has_trivial_assignment_operator;
  • has_trivial_destructor;
  • is_POD_type;

__type_traits同样是为了根据数据类型的特点而为算法选择更有效率的版本。它的实现如下

struct __true_type {//为了将__type_traits的返回值进行参数推导,所以将返回值定义为object
};

struct __false_type {
};

template <class _Tp>
struct __type_traits { 
   typedef __true_type     this_dummy_member_must_be_first;
 
 
   typedef __false_type    has_trivial_default_constructor;
   typedef __false_type    has_trivial_copy_constructor;
   typedef __false_type    has_trivial_assignment_operator;
   typedef __false_type    has_trivial_destructor;
   typedef __false_type    is_POD_type;
    
};

基本数据类型和原生指针的5个特性都被设置为true_type.

 

 

 

 

 

 

 

 

 

参考 《STL 源码剖析》 作者:侯捷

 

 

 

 

 

 

  

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值