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个硬币都可以一一数(迭代),放入到存钱罐(可以取钱的那种),那这个存钱罐就是一个迭代器对象 需求:从网络抓取各个城市气温信息,并依次显示若依次抓取较多的 ...
随机推荐
- winform中固定界面大小的方法
Step1: MaximizeBox : False MinimizeBox : False Step2: FormBoarderStyle : FixedSingle
- mongo安全:增加用户名密码
0.简述:在非auth下创建账户,然后重启 1.以不需要用户名密码的方式启动mongodb 2.运行客户端mongo,输入以下指令 show dbs;use admin;db.createRole({ ...
- Visual studio智能感知挡住了当前代码输入行
AssistX->Listboxes->Enable Visual Assist completion, suggestion and member list in .. 如果勾选了该项就 ...
- Linux uniq常用命令
-u 只显示不重复行.-d 只显示有重复数据行,每种重复行只显示其中一行-c 打印每一重复行出现次数.-f n为数字,前n个域被忽略.一些系统不识别- f选项,这时替代使用- n.
- SQL Server Analysis Services SSAS Processing Error Configurations
转载:https://www.mssqltips.com/sqlservertip/3476/sql-server-analysis-services-ssas-processing-error-co ...
- asp.net4.0在Global中的Application_Start 中直接或间接使用 HttpUtility.UrlEncode等出现异常Response is not available in this context的解决方法
HttpUtility.HtmlEncode HttpUtility.HtmlDecode HttpUtility.UrlEncode HttpUtility.UrlDecode 也会出现此异常. 这 ...
- IIS7.5打开GZip压缩,同时启用GZip压缩JS/CSS文件的设置方法[bubuko.com]
IIS7.5或者IIS7.0开启GZip压缩方法:打开IIS,在右侧点击某个网站,在功能视图中的“IIS”区域,双击进入“压缩”,如图下图: 分别勾选“启用动态内容压缩”和“启用静态内容压缩”.这样最 ...
- winform打开子窗体后,在子窗体中刷新父窗体,或者关闭子窗体刷新父窗体
winform打开子窗体后,在子窗体中刷新父窗体,或者关闭子窗体刷新父窗体,搜集了几个方法,列举如下: 一 . 所有权法 父窗体,名称为“fuForm”,在父窗体中有个公共刷新方法,也就是窗体数据初始 ...
- 【python】pickle模块
持久性的基本思想很简单.假定有一个 Python 程序,它可能是一个管理日常待办事项的程序,您希望在多次执行这个程序之间可以保存应用程序对象(待办事项).换句话说,您希望将对象存储在磁盘上,便于以后检 ...
- 服务 在初始化安装时发生异常:System.IO.FileNotFoundException: "file:///D:\testService"未能加载文件或程序集。系统找不到指定文件。
@echo.@if exist "%windir%\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe" goto INSTALL ...