STL源码--iterator和traits编程技法
第一部分 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编程技法的更多相关文章
- 迭代器iterator和traits编程技法
前言 这段时间研读SGI-STL-v2.91源码,并提炼核心代码自己实现一遍,感觉受益颇深.觉得有必要写一些文章记录下学习过程的思考,行文旨在总结,会大量参考侯捷<STL源码剖析>的内容. ...
- 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法
大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...
- STL源码分析读书笔记--第三章--迭代器(iterator)概念与traits编程技法
1.准备知识 typename用法 用法1:等效于模板编程中的class 用法2:用于显式地告诉编译器接下来的名称是类型名,对于这个区分,下面的参考链接中说得好,如果编译器不知道 T::bar 是类型 ...
- STL源码剖析——iterators与trait编程#2 Traits编程技法
在算法中运用迭代器时,很可能用到其相应类型.什么是相应类型?迭代器所指对象的类型便是其中一个.我曾有一个错误的理解,那就是认为相应类型就是迭代器所指对象的类型,其实不然,相应类型是一个大的类别,迭代器 ...
- [转载]《STL源码剖析》阅读笔记之 迭代器及traits编程技法
本文从三方面总结迭代器 迭代器的思想 迭代器相应型别及traits思想 __type_traits思想 一 迭代器思想 迭代器的主要思想源于迭代器模式,其定义如下:提供一种方法,使之能够依 ...
- STL源码之traits编程技法
摘要 主要讨论如何获取迭代器相应型别.使用迭代器时,很可能用到其型别,若需要声明某个迭代器所指对象的型别的变量,该如何解决.方法如下: function template的参数推导机制 例如: tem ...
- STL源码剖析 迭代器(iterator)概念与编程技法(三)
1 STL迭代器原理 1.1 迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...
- 《STL源码剖析》学习之traits编程
侯捷老师在<STL源码剖析>中说到:了解traits编程技术,就像获得“芝麻开门”的口诀一样,从此得以一窥STL源码的奥秘.如此一说,其重要性就不言而喻了. 之前已经介绍过迭代器 ...
- STL中的Traits编程技法
最近在看读<STL源码剖析>,看到Traits编程技法这节时,不禁感慨STL源码作者的创新能力.那么什么是Traits编程技法呢?且听我娓娓道来: 我们知道容器的许多操作都是通过迭代器展开 ...
随机推荐
- Java正则表达式详解
转自http://edu.yesky.com/edupxpt/18/2143018.shtml
- json(http://www.cnblogs.com/lanxuezaipiao/archive/2013/05/24/3096437.html)
http://www.cnblogs.com/lanxuezaipiao/archive/2013/05/24/3096437.html
- appium+python:自己写的一个滑动控件的方式
#调用方式roll_ele("ID","ele_id","7","up",3)#将控件分为7格,从底部倒数第二格向上滑动 ...
- linker command failed with exit code 1 (use -vto see invocation)
报这样的错误可能是同一个.m文件同时存在,要先把你新添加的.m文件彻底删除 Move to Trash 点击这个删除.然后clear一下,再在重新添加你所需要的文件即可解决.这次添加不要推进来,需要在 ...
- linux 查找目录或文件
查找目录:find /(查找范围) -name '查找关键字' -type d查找文件:find /(查找范围) -name 查找关键字 ·find path -option [ -print ] [ ...
- js创建节点,小试牛刀
实现如下的功能 非常简单的一个小训练. 思想: 1.首先创建text和一个button 代码如下. <body> <input type="text" id=&q ...
- js实现图片的淡入淡出
思想: 其实是运动的一种,就是当鼠标移入div中时,将div的透明度变大, 当鼠标移动出来的时候透明度变回原来. 你可以尝试写一下,不会再看看代码 <style> #div1{ width ...
- ArrayList、HashTable、List、Dictionary的演化及如何选择使用
在C#中,数组由于是固定长度的,所以常常不能满足我们开发的需求. 由于这种限制不方便,所以出现了ArrayList. ArrayList.List<T> ArrayList是可变长数组,你 ...
- teamviewer 过期解决办法
参考资料: http://blog.csdn.net/z249683156/article/details/41842271
- CSS子元素居中(父元素宽高已知,子元素未知)
<style> .container{width:400px; height:400px; position:relative;} .center{position:absolute; l ...