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

本文从三方面总结迭代器

  1.  
    迭代器的思想

  2.  
    迭代器相应型别及traits思想

  3.  
    __type_traits思想


迭代器思想

迭代器的主要思想源于迭代器模式,其定义如下:提供一种方法,使之能够依序巡防某个聚合物(容

器)所含的元素,而又无需暴露该聚合物的内部表达式。可见她的主要作用便是能够降低耦合,提高代码的

模块性。

STL的的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一贴胶着剂将它们撮合

在一,这贴胶着剂便是迭代器。迭代器的行为类似智能指针(例如标准库的auto_ptr和boost库的shared

_ptr),换句话说它重载了*
和 –> 运算符,由于设计一个适用于所有容器的迭代器是非常困难的,每个

迭代器都必须很了解容器,所以STL的每一种容器都提供了相应的专属迭代器。

STL在广义上有5种迭代器类型(不限于这5种,还可以是原生指针等,具体的容器定义自己的迭代器但

是类型是这几种之一或者是原生指针等)

  •    input
    iterator : 这种迭代器所指对象不允许外界改变,即是只读的

  •    output
    iterator : 唯写

  •  
     forward iterator :
    允许写入型算法

  •    bidirectional
    iterator : 可双向移动的迭代器

  •    random
    access iterator :  涵盖所有指针运算能力,可随机访问任何位

它们在STL中的定义如下:


template <class T, class Distance> struct input_iterator {
typedef input_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef T* pointer;
typedef T& reference;
};
struct output_iterator {
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
};
template <class T, class Distance> struct forward_iterator {
typedef forward_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef T* pointer;
typedef T& reference;
};
template <class T, class Distance> struct bidirectional_iterator {
typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef T* pointer;
typedef T& reference;
};
template <class T, class Distance> struct random_access_iterator {
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef T* pointer;
typedef T& reference;
};


迭代器相应型别及traits思想

书中把traits技法称为STL源代码的门钥,可见其十分重要。先介绍迭代器相应型别,从字面上意义来

说便是和迭代器相关的类型信息,实际上有5种常用的迭代器类型:

  •     value type : 迭代器所指对象的型别

  •     difference type :
    迭代器之间的距离型别

  •     reference type :
    迭代器引用型别

  •     pointer type : 迭代器指针型别

  •     iterator_category :
    迭代器本身的型别

STL内部需要知道当前的迭代器的这些型别信息,其所使用的方法主要是模板的参数推导、模板内嵌型

别以及模板偏特化。这里介绍下模板偏特化的概念。

模板的偏特化是指任何template参数更进一步的条件限制所设计出来的一个特化版本,例如

template<typename
T>

class
C{…}     //这个版本允许T为任何类型

template<typename
T>

class C<T*>{…}
//这个特化版本仅适用于“T为原生指针的”的情况,它比上面的更特殊

有了模板偏特化,就可以让traits萃取出原生指针(譬如vector的迭代器就是原生指针型别的)以及指

向常量的原生指针型别而不仅仅是类类型的,而负责萃取的便是 iterator_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;
};

如果是类类型的性别,用上面这个就可以获得其5个相应型别,当然这些型别必须都在相应iterator类

里面定义好(见第一节的5种迭代器的定义),那么如果不是上面5种而是原生指针等其他型别呢?这时候

就用到了模板偏特化:


//原生指针用这个
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;
};
//指向常量的原生指针用这个
template <class T>
struct iterator_traits<const T*> {
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef const T* pointer;
typedef const T& reference;
};

通过这个traits我们就可以获得任何一种iterator的相应型别,通过以下表达式即可:

iterator_traits<…>::…

说到这里有一个很重要是设计思想不得不提,就是通过函数重载在编译时决策正确的函数调用。这个问

题源于5种迭代器的类型,它们的巡防能力是不同的,例如random
acess iterator是巡防能力最强的,可以

在O(1)时间巡防指定位置,而这个用其他的迭代器可能需要O(n)。所以为了提高效率,我们应该用和迭代器

类型最匹配的算法函数去调用,能用random access
iterator的就不要用其他的。那么怎么做呢?

  •     首先通过traits获得iterator_category,这样我们能够知道当前迭代器的类型。实际上iterator_category就是用来提供这种服务的。

  •    
    在函数调用时生成相应迭代器类型的临时对象作为实参传递,编译器就会调用相应的重载函数。

为了重载函数识别,我们有对应的5种迭代器标识类:


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

继承是为了可以使用传递调用,当不存在某种迭代器类型匹配时编译器会依据继承层次向上查找进行传递。
以distance为例:


//这里category()就是为了产生临时对象
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last) {
typedef typename iterator_traits<InputIterator>::iterator_category category;
return __distance(first, last, category());
}
//input iterator 版,注意函数形参最后的类型
template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
__distance(InputIterator first, InputIterator last, input_iterator_tag) {
iterator_traits<InputIterator>::difference_type n = 0;
while (first != last) {
++first; ++n;
}
return n;
}
//random access iterator 版
template <class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type
__distance(RandomAccessIterator first, RandomAccessIterator last,
random_access_iterator_tag) {
return last - first;
}

三 __type_traits思想

有了前面的基础,我们理解到STL是非常重视效率的,而SGI STL又在其基础上实现了一个

__type_traits,根据前面的经验我们知道它是一个萃取剂,只不过它萃取的型别是:

  •     是否具备non-trivial
    default ctor?

  •     是否具备non-trivial
    copy ctor?

  •     是否具备non-trivial
    assignment operator?

  •     是否具备non-trivial
    dtor?

这里的non-trivial意指非默认的相应函数,我们知道编译器会为每个类构造以上四种默认的函数,如

果没有定义自己的,就会用编译器默认函数,如果使用默认的函数,本来就是按位拷贝我们可以使用memcpy

等函数来加快速度,提高效率。

为了使用函数重载决议,我们使用类类型来定义两种类型,__true_type和__false_type


struct __true_type {
};
struct __false_type {
};
template <class type>
struct __type_traits {
typedef __true_type this_dummy_member_must_be_first;
typedef __false_type has_trivial_default_constructor;
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;
};

这个是泛化版,STL对几乎每种内置类型都定义了相应的特化版本来制定它们的类型,整体实现不难。
 
后记:
通过迭代器的设计,我们能够看到很多非常有价值的思想,也对模板的强大有了更加深刻的认识,这些也是
继续阅读STL源码的基础。

转载地址:http://www.cnblogs.com/HappyAngel/archive/2011/04/19/2021413.html

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

时间: 2024-10-13 23:45:35

[转载]《STL源码剖析》阅读笔记之 迭代器及traits编程技法的相关文章

stl源码剖析学习笔记(二)traits编程技法简明例程

解释说明 traits侯捷老师的翻译是萃取.其目的就是在编译期进行模板调用的类型识别,从而做一些事情. 最突出的例子,我觉得不是<STL源码剖析>中"迭代器概念与traits编程技法"这一章的说明,而是stl算法中copy的实现.代码在stl源码的stl_algobase.h中. copy的最终实现,大致分为两类,一类是直接整块内存的memmove操作,另一类是一个个对象赋值.其中涉及has_trivial_assignment_operator的类型推断. 如果has_t

c++ stl源码剖析学习笔记(一)

template <class InputIterator, class ForwardIterator>inline ForwardIterator uninitialized_copy(InputIterator first, InputIterator last,ForwardIterator result) 函数使用示例 #include <algorithm> #include <iostream> #include <memory> #inclu

重温《STL源码剖析》笔记 第三章

第三章:迭代器概念与traits编程技法 迭代器是一种smart pointer auto_Ptr 是一个用来包装原生指针(native pointer)的对象,声明狼藉的内存泄漏问题可藉此获得解决. auto_ptr用法如下,和原生指针一模一样: void func() { auto_ptr<string> ps(new string("jjhou")); cout << *ps << endl; //输出:jjhou cout <<

STL 源码剖析读书笔记二:迭代器与traits

1. 迭代器概述 迭代器是一种抽象的设计概念,现实程序语言中并没有直接对应这个概念的实物.<设计模式>中对于迭代器模式的定义为:提供一种方法,使之能够依序访问某个聚合物所含的各个元素,而又无需暴露该聚合物的内部表述方式. STL 的中心思想在于:将数据容器和算法分开,彼此独立设计,再以迭代器粘合.容器和算法的泛型化在技术角度来看并不困难,C++ 的类模板和函数模板可分别达成目标.如何设计出两者的良好粘合剂,才是大难题. 迭代器是一种类似于指针的对象,而指针的各种行为中最常见也最重要的便是内容提

重温《STL源码剖析》笔记 第一章

源码之前,了无秘密. --侯杰 经典的书,确实每看一遍都能重新收获一遍: 第一章:STL简介 STL的设计思维:对象的耦合性极低,复用性极高,符合开发封闭原则的程序库. STL的价值:1.带给我们一套极具实用价值的零部件,以及一个整合的组织. 2.带给我们一个高层次的以泛型思维为基础的.系统化的.条理分明的“软件组件分类学”. 在STL接口之下,任何组件都有最大的独立性,并以所谓迭代器胶合起来,或以配接器互相配接,或以所 谓仿函数动态选择某种策略. STL六大组件:1.容器(containers

STL 源码剖析读书笔记一:空间配置器

1. STL 的空间配置器 STL 空间配置器在运用的角度来说,是最不需要介绍的,它总是隐藏在一切组件背后.但若以 STL 的实现角度而言,第一个需要理解的就是空间配置器. 根据 STL 规范,以下是 allocator 的必要接口: allocator::value_type allocator::pointer allocator::const_pointer allocator::reference allocator::const_reference allocator::size_ty

重温《STL源码剖析》笔记 第四章

源码之前,了无秘密  ——侯杰 第四章:序列式容器 C++语言本身提供了一个序列式容器array array:分配静态空间,一旦配置了就不能改变. vector: 分配动态空间.维护一个连续线性空间,迭代器类型为:Random Access Iterators 空间配置 typedef simple_alloc<value_type, Alloc> data_allocator 所谓动态增加空间大小,并不是在原空间之后接续新空间,而是以原大小的两倍另外配置一块较大 的空间,然后将原内容拷贝过来

Stl源码剖析读书笔记之Alloc细节

阅读基础: Foo *pf = new Foo; 执行了两个步骤: 1)::operator new 向系统申请内存. 2) 调用Foo::Foo()构造函数构造实例.  ==> 申请内存,构造实例. delete pf; delete; 执行了两个步骤: 1)调用Foo::~Foo()析构函数. 2). ::operator delete释放内存.         ==> 析构实例,释放内存. Stl Alloc实现: Stl为了高效利用内存,将这两部分分开,分成了四个操作( 构造::con

STL 源码剖析读书笔记三:序列式容器之 vector、list

1. STL 中的容器 容器,置物之所也.STL 容器即是将运用最广的一些数据结构实现出来.如下图所示: 上图以内缩方式来表达基层和衍生层的关系.所谓衍生,并非派生关系,而是内含关系.例如 heap 内含一个 vector,priority-queue 内含一个 heap.stack.queue都内含一个 deque,set/map/multimap/multiset 都内含一个 RB-tree,hash_x 都内含一个 hashtable. 2. 序列式容器之 vector 所谓序列式容器,其