STL源码剖析 迭代器iterator的概念 和 traits编程技法

  • iterator模式定义如下:提供一种方法,使之能够依序巡访某个 聚合物(容器)所含的各个元素,而又无需暴露该聚合物的内部表述方式.
  • STL的中心思想在于:将数据容器(containers)和算法(algorithms)分开,彼此独立设计,最后再以一帖胶着剂将它们撮合在一起。容器和算法的泛型化,从技术角度来看并不困难,C ++的class templates和 function templates可分别达成目标。如何设计出两者之间的良好胶着剂,才是大难题。

 迭代器 (iterator ) 是一种 smart pointer

  • 迭代器是一种行为类似指针的对象,而指针的各种行为中最常见也最重要的便是内容提领〈dereference)和成员访问(member access) , 因此,迭代器最重要的 编程工作就是对 operator* 和 operator-> 进行重载(overloading) 工作。关于 这一点,C++标准程序库有一个auto_ptr可供我们参考任何一本详尽的C++ 语法书籍都应该谈到auto_ptr 这是一个用来包装原生指 针 (native pointer)的对象,声名狼藉的内存漏洞(memory leak)问题可藉此获得解决. auto_ptr用法如下,和原生指针一模一样:

  • 函数第一行的意思是,以算式new动态配置一个初值为"jjhou” 的 string 对象,并将所得结果(一个原生指针)作为aUtoj>tr<Strin g > 对象的初值。注意, auto_ptr角括号内放的是“原生指针所指对象”的型别,而不是原生指针的型别. 

迭代器相 应 型 别 ( associated types )

  •  我们以func ()为对外接口,却把实际操作全部置于func_impl()之中.由于 func_impl ()是一个function template,—旦被调用,编译器会自动进行template参数推导。于是导出型别T,顺利解决了问题。
  • 迭代器相应型别(associatedtypes)不 只 是 “迭代器所指对象的型别”〜种而 已。根据经验,最常用的相应型别有五种,然而并非任何情况下任何一种都可利用上述的template参数推导机制来取得.我们需要更全面的解法。

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

  • 迭代器所指对象的型别,称为该迭代器的value iy p e .上述的参数型别推导 技巧虽然可用于value ty p e ,却非全面可用:万一 value typ e 必须用于函数的传 回值,就束手无策了,毕竟函数的*"template参数推导机制”推而导之的只是参数,无法推导函数的回返值型别。
  • 我们需要其它方法。声明内嵌型别似乎是个好主意,像这样:

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

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

  •  多了一层间接性,先前使用一个T,现在使用了两个T
  • 但这除了多一层间接性,又带来什么好处呢?好处是traits可以拥有特化版本。现在,我们令 iterator_traites 拥有一个 partial specializations 如下

  •  于是,原生指针i n t * 虽然不是一种class type,亦可通过traits取 其 value type。这就解决了先前的问题。
  • 但是请注意,针 对 “指向常数对象的指针(pointeEo-snst) ”,下面这个 式子得到什么结果
  • iterator_traits<const int*>::value_type
  • 获得的是const i n t 而非i n t . 这是我们期望的吗?我们希望利用这种机制来声 明一个暂时变量,使其型别与迭代器的value type相同,而现在,声明一个无法 赋 值 (因 c o n s t 之故)的暂时变量,没什么用!因此,如果迭代器是个pointer-to-const,我们应该设法令其value t y p e 为一个non-const型别。没问题,只要另外设计一个特化版本,就能解决这个问题:

  •  现在,不论面对的是迭代器My I t e r , 或是原生指针i n t * 或 const int*,都可以通过traits取出正确的(我们所期望的)value type。 
  • 特性萃取机”角色,萃取各个迭代器的特性。 这里所谓的迭代器特性,指的是迭代器的相应型别(associated types)。当然,若 要 这 个 “特性萃取机” traits能够有效运作,每一个迭代器必须遵循约定,自行以内嵌型别定义(nested typedef)的方式定义出相应型别(associated types) 。这是一个约定,谁不遵守这个约定,谁就不能兼容于S T L 这个大家庭。

  •  根据经验,最常用到的迭代器相应型别有五种:value 1ype, difference iype, pointer, reference, iterator catagoly。如果你希望你所开发的容器能与STL水乳交融,一定要为你的容器的迭代器定义这五种相应型别。 “特性萃取机” traits会很 •实地将原汁原味榨取出来:

 3.4.2 迭 代 器 相应型别之二:difference type

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

  •  针对相应型别difference type, traits的如下两个(针对原生指针而写的)特 化版本,以 C++内建的ptrdiff_t (定义于<cstddef>头文件)作为原生指针的 difference type:
  • ptrdiff_t是C/C++标准库中定义的一个与机器相关的数据类型。ptrdiff_t类型变量通常用来保存两个指针减法操作的结果

 3.4.3 迭代器相应型别之三:reference type

  • 从 “迭代器所指之物的内容是否允许改变”的角度观之,迭代器分为两种:不 允 许 改 变 “所指对象之内容”者,称为constant iterators,例 如 const int*pic; 允许改变"所指对象之内容”者,称 为 mutable iterators, 例 如 int* pio当我们对 一 个 mutable iterators进行提领操作时,获得的不应该是一个右值(rvalue), 应该是一个左值(lvalue), 因为右值不允许赋值操作(assignm ent), 左值才允许:

  •  在 C ++中,函数如果要传回左值,都是以by reference的方式进行,所以当p 是 个 mutable iterators时,如果其value type 是 T,那 么 * p 的型别不应该是T, 应该是T&
  • 将此道理扩充,如果p 是 一 个 constant iterators,其 value type 是 t ,那 么 * p 的型别不应该是const T , 而应该是const T&。这里所讨论的* p 的型 别,即所谓的reference type

迭代器相应型别之四:pointer type

  • pointers和 references在 C + + 中有非常密切的关联。如 果 “传回一个左值, 令它代表P 所指之物”是可能的,那 么 “传回一个左值,令它代表P 所指之物的地址”也一定可以。也就是说,我们能够传回一个pointer,指向迭代器所指之物。

  • item& 便是 Listlier 的 reference type ,而 item * 便是其 pointer type

 迭代器 相 应 型 别 之 五 :iterator_category

  • 最后一个(第五个)迭代器的相应型别会引发较大规模的写代码工程。在那之前,我必须先讨论迭代器的分类
  • 根据移动特性与施行操作,迭代器被分为五类:

  •  尽量针对图3・ 2中的某种迭代器提供一个明确定义,并针对更强化的某种迭代器提供另一种定义,这样才能在不同情况下提供最大效率。在研究STL的过程中,每一分每一秒我们都要谨记在心,效率是个重要课题。假设有个算法可接受Forward Iterator,你 以 Random Access Iterator喂给它, 它当然也会接受,因为一个Random Access Iterator必然是一个Forward Iterator(见图3-2) 。但是可用并不代表最佳!

以 advanced。 为例

  • 拿 advance () 来 说 (这是许多算法内部常用的一个函数),该函数有两个 参数,迭代器P 和数值n;函数内部将p 累进n 次 (前进n 距离)。下面有三份定义,一份针对 Input Iterator, 一份针对 Bidirectional Iterator,另一份针对 Random Access Iteratoro倒是没有针对Foiv/ardlterator而设计的版本,因为那和 针 对 Inputiterator而设计的版本完全~致。
  • 设计考虑如下:如 果 traits有能力萃取出迭代器的种类,我们便可利用这个 “迭代器类型”相应型别作为advanced 0 的第三参数。这个相应型别一定必须是一个class type,不能只是数值号码类的东西,因为编译器需仰赖它(一个型别)来进行重载决议(overloaded resolution) o 下面定义五个classes,代表五种迭代器类型:

  •  这 些 classes只作为标记用,所以不需要任何成员。至于为什么运用继承机制, 稍后再解释。现在重新设计— advance。 (由于只在内部使用,所以函数名称加 上特定的前导符),并加上第三参数,使它们形成重载:

 

  •  注意上述语法,每个— advanced 的最后一个参数都只声明型别,并未指定 参数名称,因为它纯粹只是用来激活重载机制,函数之中根本不使用该参数。如果硬要加上参数名称也可以,画蛇添足罢了。
  • 行进至此,还需要一个对外开放的上层控制接口,调用上述各个重载的_advance()。这一上层接口只需两个参数,当它准备将工作转给上述的 _advance ()时,才自行加上第三参数:迭代器类型。因此,这个上层函数必须有 能力从它所获得的迭代器中推导出其类型—— 这份工作自然是交给traits机制:

  •  任何一个迭代器,其类型永远应该落在“该迭代器所隶属之各种类型中,最强化的那个”。例如,int* 既是 Random Access Iterator,又是 Bidirectional Iterator, 同时也是Forward Iterator,而且也是Input Iterator,那么,其类型应该归属为 random_access_iterator_tag 
  • 按 说 advanced()既然可以接受各种类型的迭代器,就不应将其型别参数命 名为Inputiterator。这其实是STL算法的一个命名规则:以算法所能接受之最 低阶迭代器类型,来为其迭代器型别参数命名
  • 消 除 "单纯传递调用的函数
  • 以 class来定义迭代器的各种分类标签,不仅可以促成重载机制的成功运作 (使编译器得以正确执行重载决议,overloaded resolution), 另一个好处是,通过继承,我们 可 以 不 必 再 写 “单纯只做传递调用”的函数(例如前述的 advance() Forwarditerator版 ) 。为什么能够如此?考虑下面这个小例子,从其输出结果可以 看出端倪:

 以 d is ta n c e ()为 例

  • 关 于 “迭代器类型标签”的应用,以下再举一例。distance ()也是常用的一个迭代器操作函数,用来计算两个迭代器之间的距离。针对不同的迭代器类型,它可以有不同的计算方式,带来不同的效率。整个设计模式和前述的advance ()如出一辙:

  •  distance使用 category动态适配 InputIterator和randomAccessiterator,分别调用与之匹配的__distance函数,但是这个 distance使用的时候 <> 需要指定最小的迭代器类型,来为迭代器进行命名

  •  注意,distanced可接受任何类型的迭代器;其 template型别参数之所以命 名 为 Inputiterator,是为了遵循STL算法的命名规则:以算法所能接受之最初 级类型来为其迭代器型别参数命名.此外也请注意,由于迭代器类型之间存在着继承关系, “传递调用(forwarding) ”的行为模式因此自然存在一 一点我已在 前一节讨论过。换句话说,当客端调用distanced并使用Output Iterators或Forward Iterators 或 BidirectionaI Iterators 时,统统都会传递调用 Input Iterator 版
    的那个_ distance ( ) 函数。

std::iterator 的保证

  • 了符合规范,任何迭代器都应该提供五个内嵌相应型别,以利于traits萃取,否则便是自别于整个STL架构,可能无法与其它STL组件顺利搭配。然而写代码难免挂一漏万,谁也不能保证不会有粗心大意的时候。如果能够将事情简化,就好多了。STL提供了一个iterators class如下,如果每个新设计的迭代器都 继承自它,就可保证符合STL所需之规范:

  •  设计适当的相应型别(associated types) , 是迭代器的责任。设计适当的迭代 器,则是容器的责任.唯容器本身,才知道该设计出怎样的迭代器来遍历自己,并执行迭代器该有的各种行为(前进、后退、取值、取用成员…) 。至于算法,完全可以独立于容器和迭代器之外自行发展,只要设计时以迭代器为对外接口就行。

ite ra to r源代码完整重列

  • SGI STL<stl_iterator.h>头文件内与本章相关的程序代码。该头文件还有其它内容,是关于 iostream iterators, inserter iterators 以及 reverse iterators 的实现,将于第8 章讨论。

 我并不是很懂为啥要再包装一层

 SGI STL 的 私 房 菜 :__type_traits

  • traits编程技法很棒,适度弥补了 C++语言本身的不足。STL只对迭代器加 以规范,制定出itera to r_ tra its这样的东西。SG I把这种技法进一步扩大到迭 代器以外的世界,于是有了所谓的_ type_tra-itso 双底线前缀词意指这是SGI STL内部所用的东西,不在STL标准规范之内 
  • iterator_ traits负责萃取迭代器的特性,—type_ traits则负责萃取型别(type)的特性
  • 此处我们所关注的型别特性是指:这个型别是否具备non-trivial defaltctor ? 是否具备 non-trivial copy ctor ? 是否具备 non-trivial assignment operator?是否具备non-trivial dtor?如果答案是否定的,我们在对这个型别进行构造、析构、拷贝、赋值等操作时,就可以采用最有效率的措施(例如根本不调用身居高位,不谋实事的那些constructor, destructor), 而采用内存直接处理操作如 malloc. memcpy等等,获得最高效率。这对于大规模而操作频繁的容器, 有着显著的效率提升4。

  •  我们希望上述式子响应我们“真”或 “假”(以便我们决定采取什么策略),但其结果不应该只是个bool值,应该是个有着真/假性质的“对象”,因为我们 希望利用其响应结果来进行参数推导,而编译器只有面对Class object形式的参数, 才会做参数推导。为此,上述式子应该传回这样的东西:

  •  为了达成上述五个式子,— type_traits内必须定义一些typedefs,其值不是 _ true_type 就是 _ false_type下面是 SGI 的做法:

POD

 

例子

 

 

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446416.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中科大 计算机网络11 应用层原理

应用层大纲 传输层向应用层提供的服务&#xff0c;形式是Socket API&#xff08;原语&#xff09; 一些网络应用的例子 互联网层次中&#xff0c;应用层协议最多 流媒体应用&#xff1a;直播 网络核心最高的层次就是网络层 应用进程通信方式 C/S&#xff1a; 客户端&…

STL源码剖析 序列式容器 vector 和 ilist

Vector list 单向链表 ilistlist的删除操作&#xff0c;也只有指向被删除元素的迭代器会失效&#xff0c;其他迭代器不会受到影响

中科大 计算机网络5 接入网和物理媒体

接入网 接入网&#xff1a;把边缘&#xff08;主机&#xff09;接入核心&#xff08;路由器&#xff0c;交换机&#xff09; 骨干网【连接主机和主机】和接入网中都有物理媒体 接入方式&#xff1a;有线和无线 带宽共享/独享 接入网&#xff1a;住宅接入modem modem调制解调…

STL源码剖析 序列式容器 deque双端队列

相较于vector的内存拷贝&#xff0c;deque在内存不足时只需要进行内存的拼接操作即可&#xff0c;不需要重新配置、复制、释放等操作&#xff0c;代价就是迭代器的架构不是一个普通的指针&#xff0c;比较复杂d e q u e 的迭代器 deque是分段连续空间。维持其“整体连续”假象…

中科大 计算机网络6 Internet结构和ISP

互联网的结构 端系统通过接入ISPs接入互联网 n个ISP互相连接&#xff1a; IXP,Internet exchage point:互联网接入点&#xff0c;互联网交互点 ISP&#xff1a;互联网服务提供商&#xff0c;提供接入&#xff0c;提供网络【中国移动&#xff0c;中国电信】 ICP&#xff1a…

STL源码剖析 Stack栈 queue队列

随机迭代器用于随机数据访问&#xff0c;所以栈stack不具备此功能

中科大 计算机网络8 协议层次和服务模型

协议层次 协议层次&#xff1a;现实生活中的例子 分层 分层处理和实现复杂系统 图中&#xff0c;左边是模块&#xff0c;右边是分层 计算机的设计是分层&#xff0c;每一层实现一个或一组功能&#xff0c;下层向上层提供服务&#xff1b;但效率比较低 对等层实体通过协议来交换…

STL源码剖析 heap堆结构

heap一般特指max-heap&#xff0c;即最大的元素位于heap和array的首部 heap不提供遍历功能&#xff0c;也不提供迭代功能

中科大 计算机网络9 互联网历史

总纲 计算机网络 早期1960以前 1961-1972 NCP协议&#xff1a;相当于现在的TCP和IP协议 每个节点即是数据的源也是数据的目标

STL源码剖析 序列式容器 slist

STL l i s t 是个双向链表(double linked lis t) 。SGI STL提供了一个单向链 表 (single linked lis t) , 名 为 slist s l i s t 和 l i s t 的主要差别在于&#xff0c;前者的迭代器属于单向的Forwardlterotor, 后者的迭代器属于双向的Bidirectional Iterator.为此&#xff0…

中科大 计算机网络12 Web和HTTP

Web与HTTP 对象&#xff1a;web页中其实是对象链接 URL&#xff1a;通用资源定位符【任何对象都可以使用URL来唯一标识】 用户名&#xff1a;口令【支持匿名访问&#xff0c;用户名和口令不计】 端口&#xff1a;HTTP&#xff1a;80 FTP&#xff1a;21【使用默认端口号&#x…

STL源码剖析 关联式容器 树 红黑树、二叉搜索树、平衡二叉搜索树

所谓关联式容器&#xff0c;观念上类似关联式数据库(实际上则简单许多)&#xff1a;每笔数据(每个元素)都有一个键值(key)和一个实值(value) 2。当元素被插入到关联式 容器中时&#xff0c;容器内部结构(可能是RB-tree,也可能是hash-table)便依照其键 值大小&#xff0c;以某种…

北京大学 软件工程1 软件 软件工程 软件开发 软件工程框架

软件的定义 重新定义软件 新一代信息技术 区块链 创造性思维 软件的特点 软件的种类 支撑软件&#xff1a;VC&#xff0c;PyCharm等 应用软件&#xff1a;QQ&#xff0c;微信 软件工程的起源 软件开发的三个阶段 软件工程概念的提出 软件工程的定义 软件工程将系统化&#…

java学习_Python基础学习教程:从0学爬虫?让爬虫满足你的好奇心

Python基础学习教程&#xff1a;从0学爬虫&#xff1f;让爬虫满足你的好奇心有必要学爬虫吗&#xff1f;我想&#xff0c;这已经是一个不需要讨论的问题了。爬虫&#xff0c;“有用”也“有趣”&#xff01;这个数据为王的时代&#xff0c;我们要从这个庞大的互联网中来获取到我…

安卓rom制作教程_安卓手机TWRP_Recovery卡刷图文教程 适用于卡刷ROM,TWRP救砖

扫一扫二维码&#xff0c;关注我&#xff0c;解决刷机各种疑难杂症 ROM乐园独家支持最近有很多小伙伴问怎么去卡刷&#xff0c;卡刷的操作是什么&#xff0c;什么是卡刷&#xff0c;小编就仔细来写一下卡刷教程吧&#xff0c;记住&#xff0c;我们所说的卡刷&#xff0c;并不是…

东软 软件工程1 软件危机 软件工程 软件生命周期

软件危机 软件危机产生的原因 消除软件危机的途径&#xff1a; 软件工程历史 软件工程的概念 软件工程项目的基本目标 软件工程的基本原理 软件生命周期 软件工程的中的软件生命周期

东软 软件工程2 软件开发模型 瀑布模型 原型模型 螺旋模型 统一过程模型RUP 敏捷开发模型

软件开发过程模型 瀑布模型 原型模型 螺旋模型 统一过程模型-RUP 敏捷开发模型 敏捷开发模型&#xff1a;Scrum方法 敏捷开发模型&#xff1a;进行Scrum开发

自动点击器一秒200_做PPT还需要找模板?用这招3分钟就能自动排好PPT!

点击上图直达活动详情页&#xff0c;优惠券超 400 元&#xff01;大家好&#xff0c;我是爱挖神器的洁洁。今天我来跟大家聊聊「PPT里的神器」~我们每次做 PPT 的时候&#xff0c;经常面对的一个难题就是&#xff1a;如&#xff01;何&#xff01;排&#xff01;版 ?比如像这样…