深入理解C++迭代器

简介

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

迭代器相应型别

迭代器所指之物的型别便是其一。
假设算法中有必要声明一个以迭代器所指对象的型别的变量:
在这里插入图片描述迭代器相应型别不只是迭代器所指对象的型别一种而已,最常用的有五种,然而并非任何情况下都可以利用上述的模板参数推导机制来取得。即Traits编程技法。
//但是C++11新标准中有decltype,可以直接得到型别,以下作为了解即可。

Traits编程技法

迭代器所指对象的型别,称为该迭代器的value_type,上述模板参数型别推导技巧虽然可以用于value_type,
但是用于函数的返回值,就不行了。可以使用内嵌型别:
内嵌型别以及其不足之处:
在这里插入图片描述
不足之处:并不是所有迭代器都是类类型,原生指针就不是。如果不是类类型,就无法为它定义内嵌型别。STL绝对必须接受原生指针作为一种迭代器。所以这样还不够。

template partial specialization(模板偏特化)

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

偏特化:针对任何模板参数更进一步的条件限制所设计出来的一个特化版本。
例如:
在这里插入图片描述
下面这个类模板专门用来萃取迭代器的特性:
在这里插入图片描述
萃取int和const int
在这里插入图片描述
图解萃取:
在这里插入图片描述
若要特性萃取剂能够有效运作,每个迭代器必须遵守约定,自行以内嵌型别定义的方式定义出相应型别。这是STL的约定。
在这里插入图片描述

常用的迭代器相应型别(五种)

1.value type

任何一个打算与STL算法有完美搭配的class,都应该定义自己的value type内嵌型别,做法如上。

2.difference type

表示两个迭代器之间的距离,因此也可以用来表示一个容器的最大容量。
当我们需要迭代器T的difference type,可以这么写:
typename iterator_traits::difference_type

3.reference type

从迭代器所指之物的内容是否允许改变的角度:迭代器分为const迭代器和mutable迭代器。
当我们对一个mutable 迭代器进行提领(解引用)操作时,获得应该是一个左值T&,因为右值不允许赋值操作。
当我们对一个const 迭代器进行提领(解引用)操作时,获得也应该是一个左值const T&,因为右值不允许赋值操作。

4.pointer type

在这里插入图片描述
Item&就是迭代器的reference type,而Item*就是pointer type。
现在将这两种相应型别加入traits内:
在这里插入图片描述

5.iterator category

根据移动特性和施行(调用?)操作,迭代器分为五大类:
在这里插入图片描述
这些迭代器的分类和从属关系:
//箭头表示强化,不代表继承
在这里插入图片描述
算法中需要识别是哪种迭代器以使用相应的方法提高效率。
如advance()函数,是算法中常用的,为了对不同的迭代器使用不同的前进方式(如单项逐一前进、双向逐一前进、跳跃前进):
在这里插入图片描述
但这样的做法要在执行期间才决定使用哪一个版本,会影响程序效率,最好在编译期间就选择正确的版本。重载函数机制可以达成这个目标:

//五个作为标记用的型别
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{};

这些类只作为标记用,不需要任何成员。现在可以给函数加上第三个参数,来确定调用合适的__advance()函数:
在这里插入图片描述
__advance()函数的最后一个参数只声明型别,无需指定参数名称,只是为了激活重载机制。

此外,还需要一个对外开放的上层控制接口,调用上述重载的__advance():
在这里插入图片描述
第三个参数将产生一个临时对象,其型别对应前述五个迭代器类型之一,根据这个型别,编译器才决定调用哪一个__advance()重载函数。

为了满足上述行为,traits必须再增加一个相应的型别:
在这里插入图片描述
任何一个迭代器的类型都应该是其所隶属各种类型中(上述五种)中,最强化的那个。例如int *即使随机存取迭代器,又是双向迭代器,同时也是前向迭代器和只读迭代器。那么其类型应该是随机存取迭代器。

五种迭代器类型继承关系的妙处:

STL算法的命名规则:以算法所能接受之最初级类型来为其迭代器型别参数命名。
这样,算法就可接受任何类型的迭代器。
例如,distance()函数:
在这里插入图片描述
当调用Output_iterators或Forward_iterators或Bidirectional_iterators时,都会传递调用Input_iterator版本的那个__distance()函数,只有传Random_access_iterator时,才会调用直接计算差距的那个__distance()函数。

std::iterator的保证

为了符合规范,任何迭代器都应该提供五个内嵌相应型别,以利用traits萃取。STL提供了一个iterators class如下,如果每个新设计的迭代器都继承自它,就可保证符合STL所需之规范:
在这里插入图片描述
iterators class不含任何成员,继承它不会造成任何额外负担。后三个参数都有默认值,只需提供前两个参数即可。
例如我之前写的hn_iterator(for hn_list),如果改为正式格式,应该写成:

template<class  hn_list_node>
struct hn_list:
	public std::iterator<std::bidirectional_iterator_tag,hn_list_node>
	{...}

附加:iterator源代码完整重列

在这里插入图片描述在这里插入图片描述在这里插入图片描述

SGI STL的私房菜 :__type_traits

iterator_traits负责萃取迭代器的特性,__type_traits则负责萃取型别的特性。
是指:
这个型别是否具备non-trivial(有用的) defalt constructor?
是否具备non-trivial(有用的) copy constructor?
是否具备non-trivial(有用的) assignment constructor?
是否具备non-trivial(有用的) destory?
如果没有,我们再对这个型别进行构造、析构、拷贝、赋值等操作时,就可以采用最有效率的措施(例如根本不需调用没用的构造析构函数),而是采用内存直接处理如malloc()、memcpy()等内存操作函数,以获得最高效率。
例如另一篇空间配置器中的uninitialized_fill_n()全局函数:
在这里插入图片描述
如果不是POD型别://表示有有用的构造函数
在这里插入图片描述
如果是POD类型://表示构造函数是没用的,直接操作内存
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值