chapter3 迭代器概念与traits编程技法

1 迭代器设计思维——STL关键所在

  1. STL的中心思想在于:将容器(containers)和算法(algorithms)分开,彼此独立设计,然后使用迭代器(iterators)将它们撮合在一起。可使用class template和function template实现容器和算法的泛型化。

  2. 以算法find()为例,表明容器、算法、迭代器如何实现合作:

template <class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& value) {
    while(first != last && *first != value) ++first;
    return first;
}

2 迭代器是一种smart pointer

  1. 迭代器是一种行为类似指针的对象,其中最常见最重要的行为便是内容提领(dereference)和成员访问(member access)。因此,迭代器最重要的编程工作便是对operator*和operator->进行重载(overloading)工作
  2. 参考auto_ptr的源代码进行相应重载:
template<class T>
class auto_ptr {
public:
    //...
    T& operator*() const { return *pointee; }
    T* operator->() const { return pointee; }
    //...
private:
    T* pointee;
}
  1. 要设计出相应的迭代器需要对容器的实现细节有非常丰富的了解。因此,为了更好地实现封装,可为每一种STL容器提供专属迭代器。

3 迭代器相应型别(associated types)

  1. 相应型别(associated type):迭代器所指对象的型别
  2. 相应型别的获取:利用function template的参数推导(argument deducation)机制(局限性:无法推导函数的返回值类型)。
    在这里插入图片描述

4 Traits编程技法——STL源代码门钥

4.1 Partial Specialization(偏特化)的意义

如果class template拥有一个以上的template参数,我们可以针对其中某个(或数个,非全部)template参数进行特化。即可在泛化设计中提供一个特化版本(将泛化版本中的某些template参数赋予明确的指定)。

针对(任何)template参数更进一步的条件限制所设计出来的一个特化版本

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

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

4.2 traits编程技法

构造iterator_traits类来提取迭代器的型别,可通过进行偏特化处理,以使traits提取出正确的value type。

template<class I>
struct iterator_traits {
    typedef typename I::value_type value_type;
}

template<class I>
struct iterator_traits<T*> {	//偏特化版,迭代器是原生指针
    typedef I value_type;
}

template<class I>
struct iterator_traits<const T*> {	//偏特化版,迭代器是指向常数的指针“point to const”
    typedef I value_type;
}

在这里插入图片描述

4.3 迭代器相应型别

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

4.3.1 value_type

迭代器所指对象的型别,同上。

4.3.2 difference_type

  1. 该型别用于表示两个迭代器之间的距离,也可用于表示容器的最大容量,因为对于连续空间的容器而言,头尾之间的距离就是其最大容量。若一个泛型算法提供计数功能,例如STL的count(),其返回值就必须使用迭代器的difference type。

  2. 在针对原生指针的特化版本中,以C++内建的ptrdiff_t(定义于<cstddef>头文件)作为原生指针的difference type。

  3. 在需要任何迭代器I的difference type时,可以这么写:

typename iterator_traits<I>::difference_type;

4.3.3 reference_type

迭代器的分类(从“迭代器所指之物的内容是否允许改变”的角度):

  1. constant iterators:不允许改变“所指对象之内容”,例如const int* pic;
  2. mutable iterators:允许改变“所指对象之内容”,例如int* pi。

对mutable iterators进行解引用操作返回的应是左值(允许赋值)。因此当p是mutable iterators时,若其value type为T,那么*p的型别应是T&。此处,*p的型别即是所谓的reference type

4.3.4 pointer type

该型别是指迭代器所指的地址型别

Item& operator*() const { return *ptr; }
Item* operator->() const { return ptr; }

Item&即reference type,Item*即pointer type。

4.3.5 iterator_category

迭代器的分类(根据移动特性与施行操作

  1. Input Iterator:这种迭代器所指的对象,不允许外界改变,只读(read only);

  2. Output Iterator:只写(write only);

  3. Forward Iterator:允许 “写入型算法”,在此种迭代器所形成的区间上操作;

  4. Bidirectional Iterator:可双向移动;

  5. Random Access Iterator:前四种迭代器都只供应一部分指针算术能力(前三种支持 operator++,第四种再加上operator–),Random Access Iterator则支持所有指针的算术操作,包括 p+n,p-n,p[n],p1-p2,p1

对于上述不同的迭代器型别,通过函数重载机制来选择正确的版本,以提升效率

设计思路:使用traits机制萃取出迭代器种类,将“迭代器类型”相应型别作为函数的第三参数,以进行重载决议(overloaded resolution)。

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 {}; //原生指针也是一种random_access_iterator

上述以class来定义迭代器的各种分类标签的好处:

  1. 促成重载机制的运作
  2. 可通过继承关系而自动传递调用,避免再写“单纯只做传递调用”的函数

5 std::iterator的保证

为符合规范,任何迭代器都应该提供五个内嵌相应型别,以利于traits萃取。为简化步骤,STL提供了一个iterator class:

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

总结:

  1. 设计适当的相应型别是迭代器的责任;
  2. 设计适当的迭代器是容器的责任。唯有容器本身,才知道该设计怎样的迭代器来执行该有的行为;
  3. 算法可独立于容器和迭代器之外,只要设计时以迭代器作为对外接口即可;
  4. traits机制利用“内嵌型别”的技巧与编译器的template参数推导功能,增强了C++未能提供的型别认证的能力

6 适用于SGI STL的__type_traits

  1. iterator_traits负责萃取迭代器的特性。而SGI STL又另外提供__type_traits负责萃取型别的特性has_trivial_default_constructor, has_trivial_copy_constructor, has_trivial_assignment_operator, has_trivial_destructor, is_POD_type
  2. 响应上述特性的结果应该是个有着真/假性质的“对象”(编译器只有面对class object形式的参数时才会做参数推导
struct __true_type { };
struct __false_type { };
  1. 通过构造type_traits类来萃取型别特性:
template <class type>
struct __type_traits
{
    typedef __true_type this_dummy_member_must_be_first;
	
    //如果这个类都是trivial ctor/dtor/copy/assignment函数,我们对这个类进行构造、析构、拷贝和赋值时可以采用最有效率的方法,不调用无所事事正真的那些ctor/dtor等,而直接采用内存操作如malloc()、memcpy()等提高性能
    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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值