第一部分 iterator学习

STL iterators定义:

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

任何iteartor都应该提供5个内嵌相应型别:

1. value_type;

2. difference_type;

3. pointer;

4. reference;

5. iterator_category;

下面分别介绍5个内嵌型别:

1. value_type:

由于是泛型编程,因此容器中的数据类型并不能确定,如一个函数传进去一个参数是迭代器类型,但是在内部真正操作时却是对应的实际类型,尤其是函数的返回值更无法声明。。。因此,需要有一套机制可以让编译器知道某个iterator在传进函数时,代表的是什么类型,这样函数才可能定义。基于此思想,就有了value_type的概念,value_type代表iterator所指向的真实数据的类型。

在结构体的定义中,加入value_type,内嵌声明这个迭代器指向的类T的类型,如下所示:

template <class T>
struct MyIter // 里面加入一个类型定义value_type
{
typedef T value_type;  // 内嵌型别声明
T* ptr;
MyIter(T* p = 0): ptr(p) {}
T& operator* () const (return *ptr;)
};

template <class I>
typename I::value_type func(I ite) // 通过I::value_type返回I的类型,注意typename不能省略
{
return *ite;
}

...
MyIter<int> ite(new int(8));
Func(ite); // 可以直接运行,通过MyIter::value_type来得到返回值的类型

这段代码就是一个声明内嵌型别的例子,可以通过typename I::value_type得到迭代器指向的内容的类型。这就像是一个萃取工艺,通过把迭代器放进来就可以得到他指向的数据的类型,我们叫这种技术为traits技法,STL源码这样使用:

// 定义萃取迭代器类型的方法:
template <class T>
struct iterator_traits
{
typedef typename I::value_type value_type;
};

// 前述的函数定义就改为:
typename iterator_traits<I>::value_type func(I ite);

但是还有一个问题就是通过这段代码不能得到原生指针(如int*)对应的类型,需要使用到C++的偏特化语法来实现:

// 对于原生指针(如int*),用下面的偏特化模型
template <class T>
struct iterator_traits<T*>
{
typedef T value_type;
};

// 对于常量原生指针(如const int*),用下面的偏特化模型
template <class T>
struct iterator_traits<const T*>
{
typedef T value_type;
};

至此关于value_type的介绍就差不多了,由于其他几种内嵌型别在代码实现上是类似的,下面就简单做下介绍

2. difference_type:

用来表示两个迭代器之间的距离,也可以用来计算容器的容量。

3. pointer:

用来返回指向内容的指针。

3. reference:

用来返回指向对象的引用。

4. pointer:

用来返回指向内容的指针。

5. iterator_category:

用来返回迭代器的种类。iterator分为5个大类:input iterator(read only), ouput iterator(write only), forward iterator(支持iter++), bidirectional iterator(支持iter++, iter--), random access iterator(除前述功能外,还支持iter + n,iter - n, iter[n]等等). 大致关系如下:

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

为了一些算法能得到比较高的效率,有些时候需要知道迭代器的类型,比如找iter[n]的方法,随机访问的迭代器是最快的O(1),其他迭代器是O(n),因此对于找iter[n]的方法,需要先判断迭代器的类型,然后再调用函数,会更加有效率。

总结一下,iterator的具有一些内嵌的型别声明,这些声明在STL的泛型编程中非常有用,下面就是STL源码中的一些定义,也是提供给iterator建立者的一个模板,以免作者丢掉5个内嵌声明之一,用户在定义时,只需要从下面的模板继承即可,因为模板中只有内嵌声明,因此不会额外的增加内存等开销,方便使用。

STL提供了一个iterator class的模板,自行开发的iterator一般需要继承自这个结构体,不存在成员,不会带来其他空间成本:

// 模板,方便使用
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;
};

// 五种iterator类型
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{};

// 萃取器traits
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;
};

// 原生指针的偏特化版traits
template <class T>
struct iterator_traits<T*>
{
  typedef random_access_iterator_tag   iterator_category;
  typedef T                  value_type;
  typedef ptrdiff_t               difference_type;
  typedef T*                   pointer;
  typedef T&                    reference;
};

第二部分 traits编程技法:

第一部分提到了iterator的traits技法,从根本上讲,就是如何通过一种技术让编译器知道迭代器指向的数据是什么类型,那是不是可以通过traits技法得到一个对象或者一个类的类型或其他的信息呢?答案是肯定的。从iterator种引出了traits的概念,因此可以将这个概念扩大化来得到型别的特征,例如,有些对象是很单纯的对象不需要特别的用构造函数或析构函数调用或copy,那最有效率的方法就是直接通过内存的覆盖或内存移动,不需要通过构造函数或析构函数来操作,可以大大的提高运行效率。因此,有了下面的“类型萃取”方法(__type_traits):

template <class type>
struct __type_traits
{
typedef __true_type this_dummy_member_must_be_first; // 占位用,以防有些编译器有自己的__type_traits
typedef __false_type has_trivial_default_constructor; // 默认的都是__false_type,是最保守的定义方式,说明都是需要用构造函数等操作的
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;
};

__type_traits通过内嵌声明,可以得到一个类是否具有平凡的默认构造函数,是否有平凡的copy构造函数,是否有平凡的赋值构造函数,是否有平凡的析构函数和是否标量型。知道了这些信息以后,就可以通过更加高效的手段来实现construct, copy, assign, destruct和初始化了。

STL源码--iterator和traits编程技法的更多相关文章

  1. 迭代器iterator和traits编程技法

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

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

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

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

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

  4. STL源码剖析——iterators与trait编程#2 Traits编程技法

    在算法中运用迭代器时,很可能用到其相应类型.什么是相应类型?迭代器所指对象的类型便是其中一个.我曾有一个错误的理解,那就是认为相应类型就是迭代器所指对象的类型,其实不然,相应类型是一个大的类别,迭代器 ...

  5. [转载]《STL源码剖析》阅读笔记之 迭代器及traits编程技法

    本文从三方面总结迭代器   迭代器的思想   迭代器相应型别及traits思想   __type_traits思想 一 迭代器思想 迭代器的主要思想源于迭代器模式,其定义如下:提供一种方法,使之能够依 ...

  6. STL源码之traits编程技法

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

  7. STL源码剖析 迭代器(iterator)概念与编程技法(三)

    1 STL迭代器原理 1.1  迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...

  8. 《STL源码剖析》学习之traits编程

    侯捷老师在<STL源码剖析>中说到:了解traits编程技术,就像获得“芝麻开门”的口诀一样,从此得以一窥STL源码的奥秘.如此一说,其重要性就不言而喻了.      之前已经介绍过迭代器 ...

  9. STL中的Traits编程技法

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

随机推荐

  1. c# 串口编程

    http://news.ccidnet.com/art/32859/20100524/2067861_4.html 字节缓冲器处理类: /// <summary> /// 字节缓冲器 // ...

  2. oracle中的case when then else end 用法

    Case when 的用法,简单Case函数 简单CASE表达式,使用表达式确定返回值. 语法: CASE search_expression WHEN expression1 THEN result ...

  3. Qlik 函数说明

    集合表达式 包括 标识符 标识符 说明 1 表示应用程序中所有记录的完整集合,而不考虑选择的任何选择项.考虑维度 $ 表示当前选择项的记录.因此,集合表达式 {$} 与不陈述集合表达式的意义等同. $ ...

  4. Search history in "Maps"

    A friend of mine came to me with her iPhone yesterday. She wanted to know how to clear search histor ...

  5. SVG 使用

    SVG即Scalable Vector Graphics可缩放矢量图形,使用XML格式定义图形, 主要优势在于可缩放的同时不会影响图片的质量. SVG 在html 中常用的方法 1.使用<img ...

  6. delphi 10 seattle 安卓服务开发(三)

    delphi 10 里面的安卓服务有四种,上面的一篇文章里面的图有介绍. 今天做一个remote service 的例子.(里面一部分代码是抄别人的,如果不太清楚,自行恶补) remote servi ...

  7. ZTSD_008_1表没有某订单数据,无法回写交期

    ZTSD_008_1表没有某订单数据,无法回写交期, 取系SAP组检查执行此RFC:ZFM_FP_025_1 为什么没有将数据导进来 select * from SAPSR3.ZTSD_008_1@S ...

  8. [2015hdu多校联赛补题]hdu5378 Leader in Tree Land

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5378 题意:给你一棵n个结点的有根树.因为是有根树,那么每个结点可以指定以它为根的子树(后面讨论的子树 ...

  9. Spring 依赖注入的方式

    Spring 支持3中依赖注入的方式 1.属性注入  通过setter 方法注入Bean的属性或依赖的对象. <bean id = " " class = " &q ...

  10. Bridge(桥接)-对象结构型模式

    1.意图 将抽象部分与它的实现部分分离,使它们都可以独立地变化. 2.动机 在抽象类与它的实现之间起到桥梁作用,使它们可以独立地变化. 3.适用性 不希望在抽象和它的实现部分之间有一个固定的绑定关系. ...