STL迭代器之二:迭代器型别
如果一个迭代器要兼容stl,必须遵循约定,自行以内嵌型别定义的方式定义出相应型别。根据书中介绍,最常用到的迭代器型别有五种:value type,difference type, pointer, reference, iterator catagoly,如果你希望你开发的容器能与stl水乳交融,一定要为你的容器的迭代器定义这五种相应型别。
迭代器相应型别之一:value type
所谓value type 是指迭代器所指对象的型别。任何一个打算与stl算法有完美搭配的class, 都应该定义自己的value type内嵌型别。
迭代器型别之二:difference type
difference type用来表示两个迭代器之间的距离的类型,例如容器的容量,比如stl 的count()函数,其返回值就是迭代器的difference type:
template <class I, class T>
typename iterator_traits<I>::difference_type count(I first, I last, const T& value)
{
typedef typename iterator_traits<I>::difference_type n = ;
for (;first != last; ++first)
if (*first == value)
++n;
return n;
}
针对相应型别difference type, 原生指针使用哪个类型呢?表示空间大小一般使用unsigned int,stl为原生指针定义的特化版本为:
//带内嵌类型的迭代器
template <class I>
struct iterator_traits
{
typedef typename I::difference_type difference_type;
};
//原生指针
template <class I>
struct iterator_traits<I*>
{
typedef ptrdiff_t difference_type;
};
//const 原生指针
template <class I>
struct iterator_traits<const I*>
{
typedef ptrdiff_t difference_type;
};
这样的话当我们任何时候需要使用迭代器的difference type,可以这么写:
typename iterator_traits<I>::difference_type
迭代器型别之三:reference type
在c++中,函数如果要传回左值,都是以by reference 的方式进行,所以如果p是一个迭代器,他的value type 是T,那么*p 应该是T&(即reference type)
迭代器的解引操作operator* 返回的就是reference type,为了能融合stl,我们的迭代器也必须要定义reference 内嵌型别
stl 为reference type设计了一般版本和偏特化版:
//一般带内嵌类型的迭代器
template <class I>
struct iterator_traits
{
typedef typename::I::reference reference;
};
//原生指针
template <class I>
struct iterator_traits<I*>
{
typedef I& reference;
};
//const 原生指针
template <class I>
struct iterator_traits<const I*>
{
typedef const I& reference;
};
迭代器型别之四:pointer type
即指针类型,也就是说我们可以返回一个指针,指向迭代器所指之物,STL设计如下:
//一般带内嵌类型的迭代器
template <class I>
struct iterator_traits
{
typedef typename::I::pointer pointer;
};
//原生指针
template <class I>
struct iterator_traits<I*>
{
typedef I* pointer;
};
//const 原生指针
template <class I>
struct iterator_traits<const I*>
{
typedef const I* pointer;
};
迭代器型别之五:iterator_category
讨论前必须先知道stl迭代器的分类:
input iterator:只读迭代器(++)
output iterator:只写迭代器(++)
forward iterator:可读写迭代器(++)
bidirectional iterator:可双向移动迭代器(++,--)
random access iterator:可随机访问迭代器(+n,-n)
设计算法时,如果可能,我们应该给入参迭代器提供明确的说明,如果算法需要input iterator 我们给个random access 的当然可以,但不是最贴切的。下面列出stl算法中使用各个iterator示例:
stl中使用 input iterator示例:
template<class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val)
{
while (first!=last) {
if (*first==val) return first;
++first;
}
return last;
}
stl使用output iterator示例:
template <class OutputIterator, class Size, class T>
void fill_n (OutputIterator first, Size n, const T& val);
stl使用forward iterator示例:
template <class ForwardIterator, class T>
void replace (ForwardIterator first, ForwardIterator last,
const T& old_value, const T& new_value);
stl使用bidirectional iterator示例:
template <class BidirectionalIterator1, class BidirectionalIterator2>
BidirectionalIterator2 move_backward (BidirectionalIterator1 first,
BidirectionalIterator1 last,
BidirectionalIterator2 result);
stl使用random access iterator示例:
template <class RandomAccessIterator>
void partial_sort (RandomAccessIterator first, RandomAccessIterator middle,
RandomAccessIterator last);
当我们在使用这些算法的时候,我们是直接使用iterator,并不明确指定自己传进去的是什么iterator
例如vector:
std::vector<int> myVector;
std::find(myVector.begin(), myVector.end(),);
这个begin和end返回的是什么类型的迭代器?
其实根据vector的数据结构我们可以推测是reandom access iterator,因为vector是连续内存结构,支持随机访问,stl对vector的begin函数返回值定义如下:
An iterator to the beginning of the sequence container. If the vector object is const-qualified, the function returns a const_iterator. Otherwise, it returns an iterator. Member types iterator and const_iterator are random access iterator types (pointing to an element and to a const element, respectively).
继续回到iterator_category上,假设我们要实现一个函数,入参为迭代器p和一个整形n,函数内将p累进n次,下面有三分定义:
//只读迭代器,只支持++
template <class InputIterator, class Distance>
void advance(InputIterator&p, Distance n)
{
while (n--) ++p;
} //双向迭代器,支持++、--
template <class BidirectionalIterator, class Distance>
void advance(BidirectionalIterator& p, Distance n)
{
if (n >= )
while (n--) ++p;
else
while (n++) --p;
} //随机访问迭代器,支持随机访问
template <class RandomAccessIterator, class Distance>
void advance(RandomAccessIterator& p, Distance n)
{
p += n;
}
现在当程序员要调用advance时,应该用哪个呢?如果你是一个vector,调用第二个定义,那么效率就下降了,如果我们把三个函数合并成一个,使用一个变量区分使用哪个函数,大概代码如下:
template <class InputIterator, class Distance>
void advance(InputIterator&p, Distance n, int type)
{
if (type===input)
...
else if (type==output)
...
else
...
}
这样做我们调用时还是要多传一个参数,但我们实际上并没有这样调用advance函数,stl利用重载实现:
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag{};
struct bidirectional_iterator_tag{};
struct random_access_iterator_tag{};
//为了表示是内部函数,我们以__开头
//只读迭代器,只支持++
template <class InputIterator, class Distance>
void __advance(InputIterator&p, Distance n, input_iterator_tag)
{
while (n--) ++p;
}
//双向迭代器,支持++、--
template <class BidirectionalIterator, class Distance>
void __advance(BidirectionalIterator& p, Distance n, bidirectional_iterator_tag)
{
if (n >= )
while (n--) ++p;
else
while (n++) --p;
}
//随机访问迭代器,支持随机访问
template <class BidirectionalIterator, class Distance>
void __advance(BidirectionalIterator& p, Distance n, random_access_iterator_tag)
{
p += n;
}
//对外统一接口:(这里有个疑问:统一接口中为什么入参是InputIterator? 既然设计的初衷是接受任何类型的迭代器,就不应该指定特定类型,我们甚至可以命名成T)
template <class InputIterator, class Distance>
void advance(InputIterator& p, Distance n)
{
__advance(p, n, iterator_traits<InputIterator>::iterator_category());
}
因此,为了满足上述行为,traits还要加一个型别:
//一般带内嵌类型的迭代器
template <class I>
struct iterator_traits
{
typedef typename::I::iterator_category iterator_category;
};
//原生指针,因为我们原生指针支持随机访问,所以要定义成random access
template <class I>
struct iterator_traits<I*>
{
typedef random_access_iterator_tag iterator_category;
};
//const 原生指针
template <class I>
struct iterator_traits<const I*>
{
typedef random_access_iterator_tag iterator_category;
};
这样我们的iterator就有了五种类型,我们的traits 也要萃取迭代器的这五种类型,这样才能完全融入stl,完整代码如下:
//自定义的迭代器必须定义的五种型别
template <class Category, class T, class Distance = ptrdiff_t, class Pointer = T*, class Reference = T&>
struct iterator
{
typedef T value_type;
typedef Distance difference_type;
typedef Reference reference;
typedef Pointer pointer;
typedef Category iterator_category;
};
//把之前的五种合并在一个traits中
template <class Iterator>
struct iterator_traits
{
typedef typename Iterator::value_type value_type;
typedef typename Iterator::difference_type difference_type;
typedef typename::Iterator::reference reference;
typedef typename::Iterator::pointer pointer;
typedef typename::Iterator::iterator_category iterator_category;
};
//原生指针偏特化版
template <class Iterator>
struct iterator_traits<T*>
{
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T& reference;
typedef T* pointer;
typedef random_access_iterator_tag iterator_category;
};
//const原生指针偏特化版
template <class Iterator>
struct iterator_traits<const T*>
{
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef const T& reference;
typedef const T* pointer;
typedef random_access_iterator_tag iterator_category;
};
根据以上代码,我们可以提供一系列函数供算法使用:
//定义一个函数,获取迭代器类型
template <class Iterator>
inline typename::iterator_traits<Iterator>::iterator_category iterator_category(const Iterator&)
{
typedef typename iterator_traits<iterator>::iterator_category category;
return category();
}
//定义一个函数,获取迭代器的distance_type
template <class Iterator>
inline typename::iterator_traits<Iterator>::difference_type distance_type(const Iterator&)
{
typedef typename iterator_traits<iterator>::difference_type difference;
return difference();
} //定义一个函数,获取迭代器的value_type
template <class Iterator>
inline typename::iterator_traits<Iterator>::value_type value_type(const Iterator&)
{
typedef typename iterator_traits<iterator>::value_type value;
return value();
}
//或者返回value_type*
template <class Iterator>
inline typename::iterator_traits<Iterator>::value_type* value_type(const Iterator&)
{
typedef typename iterator_traits<iterator>::value_type* value;
return value;
}
STL迭代器之二:迭代器型别的更多相关文章
- C++迭代器之'插入迭代器
1. 定义 插入型迭代器(Insert Iterator),又叫插入器(Inserter). 2. 作用 插入迭代器的主要功能为把一个赋值操作转换为把相应的值插入容器的操作.算法库对所有在容器上的操作 ...
- STL源代码剖析(二) - 迭代器与traits技法
提要 先看一段用迭代器的代码: int a[] = {1, 2, 3, 4, 5}; vector<int> v1( a, a+5); vector<int>::iterato ...
- C++迭代器之'反向迭代器'
反向迭代器(Reverse Iterator)是普通迭代器的适配器,通过重新定义自增和自减操作,以达到按反序遍历元素的目的.如果在标准算法库中用反向迭代器来代替普通的迭代器,那么运行结果与正常情况下相 ...
- [知识点]C++中STL容器之map
UPDATE(20190416):写完vector和set之后,发现不少内容全部引导到map上了……于是进行了一定的描述补充与更正. 零.STL目录 1.容器之map 2.容器之vector 3.容器 ...
- Python之列表生成式、生成器、可迭代对象与迭代器
本节内容 语法糖的概念 列表生成式 生成器(Generator) 可迭代对象(Iterable) 迭代器(Iterator) Iterable.Iterator与Generator之间的关系 一.语法 ...
- python之函数闭包、可迭代对象和迭代器
一.函数名的应用 # 1,函数名就是函数的内存地址,而函数名()则是运行这个函数. def func(): return print(func) # 返回一个地址 # 2,函数名可以作为变量. def ...
- day14带参装饰器,迭代器,可迭代对象 , 迭代器对象 ,for迭代器 , 枚举对象
复习 ''' 函数的嵌套定义:在函数内部定义另一个函数 闭包:被嵌套的函数 -- 1.外层通过形参给内层函数传参 -- 2.验证执行 开放封闭原则: 功能可以拓展,但源代码与调用方式都不可以改变 装饰 ...
- python函数之可迭代对象、迭代器的判断
怎么判断一个对象是可迭代对象还是迭代器 例子 from collections import Iterable, Iterator lst = ['Today is Wednesday', 'Tomo ...
- python基础一 ------可迭代对象和迭代器对象
可迭代对象和迭代器对象:前者生成后者 比喻:10个硬币都可以一一数(迭代),放入到存钱罐(可以取钱的那种),那这个存钱罐就是一个迭代器对象 需求:从网络抓取各个城市气温信息,并依次显示若依次抓取较多的 ...
随机推荐
- Ionic开发实战
转自:http://blog.csdn.net/i348018533/article/details/47258449/ 折磨的两个月!Ionic从零单排,到项目发布!遇到了很多问题但都一一解决了,此 ...
- [2016.01.01]万峰文本处理专家 v2.0
<万峰文本处理专家>是一款简单易用,且功能强大的各类文本文件处理软件.1.支持多任务的处理模式,允许一次处理多个任务.2.支持正则表达式替换,替换更加强大:3.支持各类关键字的行处理操作: ...
- B 最熟悉的陌生人 (纪念当年就读的梅州市江南高级中学)
最熟悉的陌生人 作者:张慧桥 枪与玫瑰 我看了一下聊天室的名单,哈哈哈,我不禁喜出望外:蝶恋花那丫头片子挂在线上呢,真是天助我也.初时的担心一扫而光,我精神抖擞地喝下一大口咖啡,猛抽了三口烟,现在的我 ...
- svn import后,服务器上少了所有*.a文件的问题解决
转载自:http://blog.csdn.net/lwl_ls/article/details/20222051 将本地代码import到svn服务器. svn co出代码,编译却报错少了这个那个*. ...
- Eclipse下的Maven
本文转载自:http://www.cnblogs.com/zlslch/p/5882567.html 当我们无法从本地仓库找到需要的构件的时候,就会从远程仓库下载构件至本地仓库.一般地,对于每个人来说 ...
- Python 2.7.x 和 3.x 版本的重要区别
许多Python初学者都会问:我应该学习哪个版本的Python.对于这个问题,我的回答通常是“先选择一个最适合你的Python教程,教程中使用哪个版本的Python,你就用那个版本.等学得差不多了,再 ...
- ubuntu14.04 JDK安装
JDK7 安装 1 sudo apt-get install openjdk-7-jdk 2 设置安装环境 在/etc/profile中追加: # Java support # export JAVA ...
- iOS开发 - OC - duplicate symbol _OBJC / undefind symbol 错误的相关处理
前言: 作为一个iOS开发,相信大家都会遇到类似于 “duplicate symbol” 的程序报错. 对于很多新手来说,可能会有点手足无措,因为这种类型的报错一般并非是代码的逻辑错误,大部分情况下是 ...
- Spark SQL Example
Spark SQL Example This example demonstrates how to use sqlContext.sql to create and load a table ...
- Win8.1安装Visual Studio 2015提示需要KB2919355
http://www.microsoft.com/zh-cn/download/details.aspx?id=42335 安装说明: 1.若要开始下载,请单击“下载”按钮,然后执行以下操作之一,或者 ...