迭代器iterator和traits编程技法

时间:2022-10-19 22:42:10

前言

这段时间研读SGI-STL-v2.91源码,并提炼核心代码自己实现一遍,感觉受益颇深。觉得有必要写一些文章记录下学习过程的思考,行文旨在总结,会大量参考侯捷《STL源码剖析》的内容。

迭代器概述

迭代器定义:提供一种方法,使之能够依序巡访某个聚合物(容器)所含的各个元素,而无需暴露该聚合物的内部表述方式。

STL的中心思想在于:将数据容器和算法分开,彼此独立设计,然后再以一贴胶着剂撮合在一起。在STL中,迭代器就扮演着胶着剂的作用。迭代器是一种类似指针的对象,而指针的各种行为中最常见也是最重要的便是解引用和成员访问,因此迭代器最要的编程工作就是对operator*和operator->进行重载。

泛型编程和面向对象编程

认识迭代器之前我觉得有必要了解一下C++中两个非常重要的编程范式,即GP(泛型编程)OOP(面向对象编程)。

泛型编程:亦称为"静态多态",多种数据类型在同一种算法或者结构上皆可操作,其效率与针对某特定数据类型而设计的算法或结构相同, 具体数据类型在编译期确定,编译器承担更多,代码执行效率高。在STL中利用GP将methods和datas实现了分而治之。

面向对象编程:将methods和datas关联到一起 ,也就是方法和成员变量放到一个类中实现,通过继承的方式,利用虚函数表(virtual)实现运行时类型判定,也叫"动态多态", 由于运行过程中需根据类型去检索虚函数表,因此效率相对较低。

STL全部采用泛型编程,因此,相对来说STL的效率较高且易于维护。

萃取编程技法

在STL算法中需要用到迭代器相应型别,即迭代器所指对象的型别。但C++只支持 sizeof() 并未支持 typeof() 。STL的解决方法之一是利用 function template 的参数推导机制,但 value type 必须用于函数的返回值时 function template 就束手无策。STL利用class类的声明内嵌型别以及针对原生指针的偏特化方式,设计迭代器的萃取机萃取迭代器的特性,解决用于函数返回值的问题,value type 是迭代器的特性之一。下面是STL源码利用class类的声明内嵌型别设计的萃取机,也是萃取机的泛化版本:

//STL萃取器的泛化版本
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;
};

因为原生指针不是class type,无法为它定义内嵌型别。STL利用C++偏特化的方式为泛化设计提供一个特化版本,即将泛化版本中的某些template参数赋予明确的指定。下面是STL源码针对原生指针T*设计的特化版本萃取机,源码还设计了一个const T*原生指针的特化版本,与T*类型这里不再呈现源码:

//针对T*类型的原生指针设计的特化版本
template<class T>
struct iterator_traits<T*> {
typedef random_access_iterator_tag iterator_categoty;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
};

然后STL源码设计了一个获取迭代器 value type 的全局函数,将迭代器作为参数,函数可返回迭代器的型别,具体源代码如下:

template<class Iterator>
inline typename iterator_traits<Iterator>::value_type*
value_type(const Iterator&) {
//根据掺入的Iterator是class type还是T*或const T*原生指针调用泛化或特化的iterator_traits
return static_cast<typename iterator_traits<Iterator>::value_type*>();
}

源码还设计了获取迭代器其他特性 iterator_categoty 和 distance_type 的全局函数,和value_type类似,源码如下:

template<class Iterator>
inline typename iterator_traits<Iterator>::iterator_category
iterator_category(const Iterator&) {
typedef typename iterator_traits<Iterator>::iterator_category category;
return category();
} template<class Iterator>
inline typename iterator_traits<Iterator>::difference_type*
distance_type(const Iterator&) {
return static_cast<typename iterator_traits<Iterator>::difference_type*>();
}

这种萃取编程技法是STL迭代器实现最重要的方法,萃取机能够萃取各种迭代器的特性。正是这种技法弥补了C++没有typeof()功能的缺陷,让迭代器能够真正脱离于容器,应用于算法中去。

迭代器分类和从属关系

STL源码中定义五个classes代表五种迭代器的型别,定义如下:

//五种迭代器型别
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 {};

这些classes只作为标记用,所以不需要任何成员。这五种迭代器型别的关系如下图所示:

迭代器iterator和traits编程技法

迭代器型别巧用--函数重载

上图能够非常明确看出迭代器的分类和从属关系,设计算法时,如有可能,我们尽量针对上图的某种迭代器提供一个明确定义,并针对更强化的某种迭代器提供另一种定义,保证在不同情况下提供最大效率。在源码中 advance 算法的实现非常能体现这一设计原则。函数入口和针对不同迭代器不同的定义的源码如下:

//advance函数入口
template<class InputIterator, class Distance>
inline void advance(InputIterator& i, Distance n) {
__advance(i, n, iterator_category(i));
} //InputIterator类型定义
template<class InputIterator, class Distance>
inline void __advance(InputIterator& i, Distance n, input_iterator_tag) {
while (n--) ++i;
} //BidirectionalIterator类型定义
template<class BidirectionalIterator, class Distance>
inline void __advance(BidirectionalIterator& i, Distance n,
bidirectional_iterator_tag) {
if (n >= )
while (n--) ++i;
else
while (n++) --i;
} //RandomAccessIterator类型定义
template<class RandomAccessIterator, class Distance>
inline void __advance(RandomAccessIterator& i, Distance n,
random_access_iterator_tag) {
i += n;

上面的源码分别对 InputIterator、BidirectionalIterator 和 RandomAccessIterator 三种类型的迭代器设计了不同的 __advance 定义,根据第三参数的不同,使函数形成重载。第三参数只是声明型别并未指定参数名称,因为它纯粹只是用来激活重载机制,函数根本不使用该参数。从源码可以看出 advance 函数根据 iterator_category 获取迭代器型别而重载不同的 __advance 函数,而函数的重载过程是发生在编译期,所以函数的运行效率非常高。

行文至此,我认为已经将STL源码中迭代器和和萃取编程技巧的核心思想总结出来,具体的源码实现可以参考我github的实现:https://github.com/evenleo/sgi-stl-even,关于迭代器的源码在 stl_iterator.h 文件中。

迭代器iterator和traits编程技法的更多相关文章

  1. STL源码--iterator和traits编程技法

    第一部分 iterator学习 STL iterators定义: 提供一种方法,使之能够依序巡访某个聚合物(容器)所含的各个元素,而又无需暴露该聚合物的内部表达方式. 任何iteartor都应该提供5 ...

  2. 迭代器概念与traits编程技法

    //迭代器是一种smart pointer template<typename T> class ListItem { public: T value() const { return _ ...

  3. STL源码分析读书笔记--第三章--迭代器(iterator&rpar;概念与traits编程技法

    1.准备知识 typename用法 用法1:等效于模板编程中的class 用法2:用于显式地告诉编译器接下来的名称是类型名,对于这个区分,下面的参考链接中说得好,如果编译器不知道 T::bar 是类型 ...

  4. 迭代器(iterator) 与 traits 编程技法

    看了候哥的<STL源码剖析>的迭代器那一章,在这里将思路稍微疏理一下 迭代器 迭代器模式的定义:提供一种方法,在不需要暴露某个容器的内部表现形式情况下,使之能依次访问该容器中的各个元素. ...

  5. STL——迭代器与traits编程技法

    一.迭代器 1. 迭代器设计思维——STL关键所在 在<Design Patterns>一书中对iterator模式定义如下:提供一种方法,使之能够依序巡访某个聚合物(容器)所含的各个元素 ...

  6. 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法

    大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...

  7. STL Traits编程技法

    traits编程技法大量运用于STL实现中.通过它在一定程度上弥补了C++不是强型别语言的遗憾,增强了C++关于型别认证方面的能力. traits编程技法是利用“内嵌型别”的编程技法和编译器的temp ...

  8. STL中的Traits编程技法

    最近在看读<STL源码剖析>,看到Traits编程技法这节时,不禁感慨STL源码作者的创新能力.那么什么是Traits编程技法呢?且听我娓娓道来: 我们知道容器的许多操作都是通过迭代器展开 ...

  9. STL源码之traits编程技法

    摘要 主要讨论如何获取迭代器相应型别.使用迭代器时,很可能用到其型别,若需要声明某个迭代器所指对象的型别的变量,该如何解决.方法如下: function template的参数推导机制 例如: tem ...

随机推荐

  1. 历尽磨难之PL&sol;SQL链接Oracle数据库

    说起来都是泪啊,上司布置的任务需要远程连接Oracle数据库,说实话这又是我人生中的第一次.我听到以后觉得不是什么大问题,然而我错了..错的很厉害! 我搞了一天一夜才弄好,这里面原因有很多,大体来讲还 ...

  2. 上下文管理、线程池、redis订阅和发布

    一:上下文管理: 对于一些对象在使用之后,需要关闭操作的.比如说:socket.mysql数据库连接.文件句柄等. 都可以用上下文来管理. 语法结构: Typical usage: @contextm ...

  3. Debian MySQL 卸载和安装 PHP安装

    如果是apt-get安装的 sudo apt-get --purge remove mysql-serversudo apt-get --purge remove mysql-clientsudo a ...

  4. SmartZoneOCR识别控件免费下载地址

    SmartZone™光学字符识别工具包,使开发人员能够进行带状区域光学字符识别,带状区域在表单处理应用程序中经常使用.本产品所包含的.NET控件以及ActiveX COM组件在内部使用两种单独的识别技 ...

  5. 自定义延时查询控件---valen

    当查询已经成标配 查询是已成为每个应用常用的功能,也正是这样前端后对查询的设计需求也日益增加,本文针对前端(Android端)查询控件做一个例子: 控件设计与逻辑 产品的设计UI图; 要达到如下 1| ...

  6. 缩略图类库--ThumbLib使用简介

    //加载类库文件 require_once 'path/to/ThumbLib.inc.php'; //实例化类库,传入你要处理的图片的地址可以是网络地址,也可以是本地地址 $thumb = PhpT ...

  7. 优雅使用 illuminate&sol;database 包中的 Collection

    优雅使用 illuminate/database 包中的 Collection 或许你很抵抗使用 Laravel , 但是你没有理由不喜欢使用 illuminate/database.这是一个 ORM ...

  8. mysql用户链接数

    show status like 'Threads_connected';  当前连接数 show variables like '%max_connections%'; 最大链接数 set GLOB ...

  9. checkbox事件的变化

    <input type="checkbox" checked={this.state.checked} onChange={this.checkedChangeHandler ...

  10. 因为没有安装xcode,得不到xcode证书报错

    新接的一个项目.在从gitlab上clone代码的时候报错: Agreeing to the Xcode/iOS license requires admin privileges, please r ...