第一部分 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. fw: openstack

    OpenStack既是一个社区,也是一个项目和一个开源软件,它提供了一个部署云的操作平台或工具集.其宗旨在于,帮助组织运行为虚拟计算或存储服务的云,为公有云.私有云,也为大云.小云提供可扩展的.灵活的 ...

  2. Android软件更新安装。

    app的开发有一个问题是避免不了的,那就是软件的升级维护. 这里我在查过一些资料和写了一个升级帮助类.使用很方便.直接导入就可以了. ( VersionBean.class为更新地址返回的数据对象,我 ...

  3. ORACLE 常用函数 日期/时间函数

    ---------------------------------------------日期/时间函数----------------------------------------------- ...

  4. linux mono环境

    安装好 CentOS 6.5 之后 1.更新系统 在命令行下执行 yum –y update 2.安装必要的软件 yum -y install gcc gcc-c++ bison pkgconfig ...

  5. 【转】操作权限不够?教你开启Win7管理员帐户

    在Win7中进行一些设置,或修改某些文件时,经常会弹出当前帐户没有操作权限的提示,即使已经是管理员账户也不行.事实上,出于安全方面的考虑,默认情况下Win7系统的系统管理员Administrator账 ...

  6. The Magic only works with total devotion of one's heart

    The Magic only works with total devotion of one's heart All tools and equipments are useless without ...

  7. tomcat的乱码问题

    String filename=request.getParameter("filename"); filename=new String(filename.getBytes(&q ...

  8. 27、初步探索echarts源码

    1.首先发现随笔中凡是和echarts相关的点击率都特别高,于是乎就接着写了echarts因为感觉要转点击率 首先声明我并不是专业做前端的,所以如果有些说得不对的地方,希望前端大神们出来指正 首先发现 ...

  9. 使用css3中calc()进行自适应布局

    calc()能做什么? calc()可以通过计算得到元素的宽度或者高度,让我们很容易进行自适应布局. 你可以为一个div元素,使用百分比.em.px和rem单位值计算出其宽度或者高度,比如说“widt ...

  10. CodeForces 675C Money Transfers(贪心+奥义维护)

    题意:n个银行. 其中存款有+有-. 总和为0. n个银行两两相邻((1,n),(1,2)...(n-1,n)); 问最少移动几次(只能相邻移动)能把所有数变为0. 分析:思路很简单,起始答案算它为n ...