• 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

 

例子

 

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

  1. SGL STL源码剖析——迭代器

    SGL STL源码剖析--迭代器 迭代器 迭代器的型别 Traits的作用 迭代器相应的五种型别 __type_traits 迭代器 在我们使用STL容器的时候,迭代器是非常常见的,STL将容器和算法 ...

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

    迭代器:依序巡防某个聚合物(容器)所含的各个元素,但是不需要暴露这个聚合物的内部表述方式 核心思想:将容器和算法分开,彼此独立设计 容器和算法的泛型化,均可以使用模板,使用迭代器连接容器和算法 例子 ...

  3. STL源码剖析---迭代器失效小结

    迭代器(iterator)是一个可以对其执行类似指针的操作(如:解除引用(operator*())和递增(operator++()))的对象,我们可以将它理解成为一个指针.但它又不是我们所谓普通的指针 ...

  4. 【STL源码剖析】迭代器

    [STL源码剖析]迭代器 第3章 迭代器(iterators)与traits编程技法(<STL源码剖析> ) 3.1 迭代器设计思维--STL关键所在 3.2 迭代器(iterator)是 ...

  5. 【STL源码剖析】list模拟实现 | 适配器实现反向迭代器【超详细的底层算法解释】

    今天博主继续带来STL源码剖析专栏的第三篇博客了! 今天带来list的模拟实现! 话不多说,直接进入我们今天的内容! 前言 那么这里博主先安利一下一些干货满满的专栏啦! 手撕数据结构https://b ...

  6. STL(C++标准库,体系结构及其内核分析)(STL源码剖析)(更新完毕)

    文章目录 介绍 Level 0:使用C++标准库 0 STL六大部件 0.1 六大部件之间的关系 0.2 复杂度 0.3 容器是前闭后开(左闭右开)区间 1 容器的结构与分类 1.1 使用容器Arra ...

  7. 【有点狂的手撕STL】STL源码剖析精读 000

    STL源码剖析精读 前言 通过刷题感受到了C++中STL的妙用,十分的想要提高自己对于STL的理解以及运用能力,因此开设此专栏,并希望能够带领大家一起感受C++中STL的魅力. 一.STL简介 STL ...

  8. 《STL源码剖析》学习--traits

    在本书迭代器一节提到了Traits编程技法,将其誉为stl源码门匙,在<C++编程思想>一书中,将其列为模板编程中的习语"特征",这里进行总结. traits技术 也往 ...

  9. STL源码剖析 数值算法 copy 算法

    copy复制操作,其操作通过使用assignment operator .针对使用trivial assignment operator的元素型别可以直接使用内存直接复制行为(使用C函数 memove ...

最新文章

  1. 提高C++性能的编程技术笔记:跟踪实例+测试代码
  2. C# Span 源码解读和应用实践
  3. Linux多线程贝叶斯建树教程,建树经验.doc
  4. mysql join 循环_关于mysql联表的内嵌循环操作nested loop join中on和where执行顺序问题...
  5. 滴滴接盘小蓝单车,押金问题谁来负责?
  6. Win10电脑如何批量修改文件名
  7. Spring整合Struts2的两种方式
  8. Native Boot 从一个 VHD 引导系统的相关说明
  9. 海康 hikvision SDK 初始化、登录、布防、监听
  10. SuperSlide插件轮播图展示
  11. 设置二级域名解析到同IP不同端口
  12. 【BSC】使用Python玩转PancakeSwap(入门篇)
  13. Java –显示所有ZoneId及其UTC偏移量
  14. ue4 unreal4 json序列化工具 数据转成字符串等
  15. 知乎爬虫请求头参数x-zse-96(代码可直接运行)
  16. 短视频平台类的社交媒体市场现状与发展前景到底如何?
  17. Java8 jvm参数
  18. NLP任务中, 被pad和unk的向量应该赋值为zero还是random呢?
  19. 10岁男童高考获566分或被大学破格录取(图)
  20. java里void的意思_void的用法和意义

热门文章

  1. educoder平台_22个在线平台,2.4万门网课
  2. 【转】TCP/IP协议到底在讲什么?【乐搏TestPro】
  3. Sharepoin学习笔记 —架构系列--02 Sharepoint的处理(Process)与执行模型(Trust Model) 1
  4. ASP.NET MVC 入门5、View与ViewData
  5. 流水灯verilog实验原理_IC设计实例解析之“流水线技术”
  6. List,Map,实体类,字符串相互转换
  7. 小学计算机制作表格教案,小学信息技术《表格的制作》教案
  8. *【HDU - 4272 】LianLianKan (dfs 或 状压dp,贪心不行)
  9. 【CodeForces - 768C】Jon Snow and his Favourite Number(思维,技巧,套路,数学异或,循环节,trick)
  10. 【 POJ - 2033 】Alphacode (dp,有坑)