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个硬币都可以一一数(迭代),放入到存钱罐(可以取钱的那种),那这个存钱罐就是一个迭代器对象 需求:从网络抓取各个城市气温信息,并依次显示若依次抓取较多的 ...
随机推荐
- redis 3.2.6 on ubuntu 14.04
1. official site: https://github.com/antirez/redis/releases 2. compile and setup tar zxf redis-3.2.6 ...
- 用Sublime 3作为React Native的开发IDE- 转
转-http://www.cnblogs.com/wangshuo1/p/react_native_02.html Sublime Text是一个代码编辑器.也是HTML和散文先进的文本编辑器.漂亮的 ...
- NSString和NSMutableString常用方法+NSArray常用代码 (转)
常见的NSString和NSMutableString方法: NSString方法: [plain] view plaincopy +(id) stringWithContentsOfFile:p ...
- iPhone4下window各个部分的高度
- 关于lemon oa的数据库
lemonOA的数据库默认用的是hsqldb,这个数据库还是第一次听说,暂且不论. 也就说Lemon OA默认使用HSQLDB数据库,是嵌入式的数据库不需要单独安装. lemon-1.4.0\weba ...
- 右键添加 CMD 命令提示符
# 右键添加 CMD 命令提示符 当然是修改注册表 - 打开注册表编辑器(按下Win+R打开运行对话框,输入regedit),找到[HKEY_CLASSES_ROOT/Folder/shell] ...
- Python使用struct处理二进制
有的时候需要用python处理二进制数据,比如,存取文件,socket操作时.这时候,可以使用python的struct模块来完成.可以用 struct来处理c语言中的结构体. struct模块中最重 ...
- C#中Validating和Validated事件
http://blog.sina.com.cn/s/blog_6116673a0100fpeo.html 待解读
- logcat--目录
代码实现获取log日志和logcat使用方法:http://www.apkbus.com/android-128263-1-1.html logcat命令使用方法和查看android系统日志缓冲区内容 ...
- 用读写锁三句代码解决多线程并发写入文件 z
C#使用读写锁三句代码简单解决多线程并发写入文件时提示“文件正在由另一进程使用,因此该进程无法访问此文件”的问题 在开发程序的过程中,难免少不了写入错误日志这个关键功能.实现这个功能,可以选择使用第三 ...