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编程技法呢?且听我娓娓道来: 我们知道容器的许多操作都是通过迭代器展开 ...
随机推荐
- selenium2 WebDriver 在asp.net项目中的应用
selenium2 WebDriver是一款跨平台的 自动化测试工具,它可以操纵浏览器,模拟用户行为,非常方便用户进行自动化测试. .net项目使用它,首先要通过 Visual Studio 的 nu ...
- Entity Framwork(EF) 7——在Controller内获取指定字段的值
一.开发背景: 在用户登录的时候,验证用户和密码是否正确.验证通过后将用户名和用户ID保存下来以便后续数据更新时使用. 二.用户验证方法: 1.创建DBContext 对象. ApplicationD ...
- PCI Express(四) - The transaction layer
原文出处:http://www.fpga4fun.com/PCI-Express4.html 感觉没什么好翻译的,都比较简单,主要讲了TLP的帧结构 In the transaction layer, ...
- Android 环境常见问题
1.启动adb时报: adb server is out of data.kill... ADB server didn't ACK *failed to start daemon* er ...
- Python 之WEB前端插件
1.Font Awesome ---- 设计字体,图标 2.EasyUI ---- 各种功能 3.JqueryUI ---- 类似EasyUI 4.bootstrap ---- 必须引入JQuery( ...
- jquery remove/add css
<input type="submit" class="btn btn-primary" id="submit" value=&quo ...
- Brn系列商城3.0测试版正式发布,欢迎大家下载测试
BrnShop商城3.0测试版和BrnMall商城3.0测试版正式发布,欢迎大家下载测试(点击下载).测试结束后将会发布正式版和MySQL版本. 测试过程中发现bug欢迎大家给我们反馈,反馈邮箱:br ...
- C/ C++ 常见编程问题
C 中容易忽略的问题 1.在C语言中,浮点型变量分为两类: a. 单精度型:类型说明符为float, 在Turbo C 中占4个字节(32位)内存空间,其数值范围为3.4E-38~3.4E+38,可提 ...
- C# 根据包含文件的路径和文件的名称的字符串获取文件名称的几种方法
C# 截取带路径的文件名字,扩展名,等等 的几种方法 C#对磁盘IO操作的时候,经常会用到这些,路径,文件,文件名字,文件扩展名. 之前,经常用切割字符串来实现, 可是经常会弄错. 尤其是启始位置,多 ...
- The certificate used to sign “AppName” has either expired or has been revoked. An updated certificate is required to sign and install the application解决
问题 The certificate used to sign "AppName" has either expired or has been revoked. An updat ...