迭代器(iterators)与traits编程技巧

1 迭代器基础

迭代器(iterator):在不暴露容器内部的前提下,能依序访问容器的每个元素。
迭代器是STL的关键,STL的中心思想是:将容器与算法分开,彼此独立,然后用粘合剂进行关联,迭代器就是这个粘合剂。

通过平时对STL的应用可以发现,每个容器都有自己的迭代器,例如:vector<int>::iterator ;list<int>::iterator等,迭代器总是依附于容器的。为了将实现细节封装,STL将迭代器的开发交给了容器的设计者。所以,每种STL中每种容器都有自己专属的迭代器。

2 traits技巧与容器的内嵌类型

上篇博客讲配置器时,有提过traits编程技巧的一个应用,_type_traits。traits技巧在STL中应用的非常广泛,这里通过例子再次加深对traits技巧的理解。
问题1:
如果某一个算法,传入迭代器参数,需要用到“迭代器所指对象”的类型,怎么办?

//func是对外接口
template<typename I>
void func(I it)
{
	//将主要工作交给impFunc完成
	//关键在于将it所指对象传递过去
	//通过模板"参数推导"功能可获得迭代器所指对象的类型
	implFunc(it,*it);
}
template<typename I ,typename T>
void implFunc(I it,T val)
{
	T tmp;//可以构造T类型的对象了
	//主要工作在这里完成
}
问题2:
模板只能推导参数类型,不能推导返回类型。如果上述算法需要返回 迭代器所指对象的类型,又该怎么办??


这里就要用到类模板的内嵌类型了!!

template<typename T>
class MyIter
{
	public:
	typedef T value_type;//声明内嵌类型(nest type)
	T *ptr;
	//.....
}
template<truename I>
typename I::value_type//返回类型 
func(I it)
{
	//实际工作
}

问题3:
对于原生指针,例如int *,char *,double *等。声明内嵌类型是实现不了的,那么上面的func()函数对原生指针就运行不了了。如果想让func()在支持迭代器的同时也支持原生指针,又该怎么办呢???

这里就得用到traits编程技巧了!!!


//iterator_traits的一般化
template<typename I>
struct  iterator_traits
{
	typedef  typename I::value_type  value_type; 
}
//针对 I为原生指针 iterator_traits的特化版本

template<typename T>
struct  iterator_traits(T *)
{
	//对于原生指针 value_type 就是T
	typedef T value_type;
}

//所以func()函数就可以这样写
template<typename I>
typename iterator_traits<I>::value_type
func(I it)
{
	//这样不管I是迭代器类型,还是原生指针
	//该函数都能正常工作
	return *ite;
}



STL为了让iterator_traits能正常运行,要求每个迭代器都必须定义自己的内嵌类型。


3 迭代器的相应类型

除了上面value_type外,迭代器还有四个相应类型
value type 、difference type 、pointer、reference、iterator catagoly>。
value type:表示迭代器所指对象的类型。
difference type :表示两个迭代器之间的距离。
pointer :指向迭代器操作对象的指针。
reference :迭代器所指对象的引用类型。
iterator catagoly:表示迭代器的类型。下面会详细介绍。
和上述的value type一样,每种容器的迭代器都必须定义这五种类型。然后,iterator_traits就可以如下定义:

//iterator_traits的默认版本
template<typename I>
class iterator_traits
{
	//每种容器的迭代器都必须定义value_type,value_type,pointer,reference,iterator_category
	typedef typename I::value_type value_type;
	typedef typename I::difference_type value_type;
	typedef typename I::pointer pointer;
	typedef typename I::reference reference;
	typedef typename I::iterator_category iterator_category;
}
//针对 I为 原始指针的特化版本
template<typename T>
class iterator_traits<T *>
{
	typedef  T value_type;
	typedef ptrdiff_t difference_type;//build-in 类型ptrdiff_t定义在<cstddef>头文件中
	typedef T* pointer;
	typedef T & reference;
	typedef random_access_iterator_tag iterator_category;//关于iterator_category,下面会详细介绍
}
//针对 I为 const原始指针的特化版本
template<typename T>
class iterator_traits<const T *>
{
	typedef  T value_type;
	typedef ptrdiff_t difference_type;//build-in 类型ptrdiff_t定义在<cstddef>头文件中
	typedef const T* pointer;
	typedef const T & reference;
	typedef random_access_iterator_tag iterator_category;//关于iterator_category,下面会详细介绍
}

迭代器的类型 iterator_category

迭代器被分为五类:

Input iterator :只读(read only)

Output iterator:只写(write only)

Forward Iterator:读写(read and write)

Bidirectional Iterator:双向迭代器,可前进可后退。

Random Access Iterator:可随机访问,跳跃式,p+n,p-n,p[n],p1-p2,p1<p2等都支持。

举个例子说明iterator_traits怎么处理五种类型迭代器的,同时对traits编程技巧加上印象。

实现advance(I,n)函数,两个参数I表示迭代器,n表示前进的距离。针对不同类型的迭代器,调用效率最高的函数版本。

思路一:

定义多个版本,在对外接口中判断调用哪个版本。

//针对input iterator
template<typename InputIterator,typename Distance>
void _addvance_II(InputIterator & i,Distance n)
{
	while(n--)++i;
}
//针对 bidirectional  iterator
template<typename Bidirectional,typename Distance>
void _addvance_IB(Bidirectional & b,Distance n)
{
	if(n>=0)
        while(n--)++b;
	else	
		while(n++)--b;
}
//针对Random  Access Iterator
template<typename RandomAccessIterator,typename Distance>
void _addvance_RAI(RandomAccessIterator & r,Distance n)
{
	r+=n;
}

//对外接口
template<typename InputIterator,typename Distance>
void advance(InputIterator i,Distance n)
{
	//在这里判断i到底是哪种类型的迭代器
	if(is_input_iterator(i))
		_addvance_II(i,n);
	else if(is_bidirectional_iterator(i))
		_addvance_IB(i,n);
	else
		_addvance_RAI(i,n);
}

这种方法有个严重的缺陷:在运行期间才确定执行哪个版本,影响效率,最后在编译期间就能决定调用哪个函数。函数重载可解决这个问题,1.为了实现重载机制需要提供额外的参数来区分不同函数;2.然后再在对外接口内确定该额外参数的类型。


//五个作为标记的类型
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag:public input_iterator_tag{};
struct bidirectional_iterator:public forward_iterator_tag{};
struct random_access_iterator_tag:public bidirectional_iterator{};

//针对input iterator
template<typename InputIterator,typename Distance>
void _addvance(InputIterator & i,Distance n,input_iterator_tag)
{
	while(n--)++i;
}
//forward iterator版本不用定义,因为它的实现与 input iterator版本完全一样
//上面的五个类型标记的继承关系使得 当传进来的迭代器类型是forward iterator时
//由于没有定义该版本的_advance,所以会调用基类input iterator版本的_advance.

//针对 bidirectional  iterator
template<typename Bidirectional,typename Distance>
void _addvance(Bidirectional & b,Distance n,bidirectional_iterator)
{
	if(n>=0)
        while(n--)++b;
	else	
		while(n++)--b;
}
//针对Random  Access Iterator
template<typename RandomAccessIterator,typename Distance>
void _addvance(RandomAccessIterator & r,Distance n,random_access_iterator_tag)
{
	r+=n;
}

//对外接口
template<typename InputIterator,typename Distance>
void advance(InputIterator i,Distance n)
{
	//这里利用iterator_traits判断调用哪个版本的_advance
	_addvance(i,n,iterator_traits<InputIterator>::iterator_category());
}


为了规范,任何迭代器都需提供上述五个内嵌类型,以利用traits提取特性。为了方便,STL提供了一个基类 iterator,每个特定的迭代器只需继承该基类即可。


template<typename Category,

         typename T,
		 typename Distance=pdfdiff_t,
		 typename Pointer= T*,
		 typename Reference=T&;>
class iterator
{
	typedef Category iterator_category;
	typedef T value_type;
	typedef Distance difference_type;
	typedef Pointer pointer;
	typedef Reference reference;
}

//例如 list的迭代器,类型为forward iterator
template<typename Item>
class struct ListIter:public std::iterator<std::forward_iterator_tag,Item>
{
	//......
}








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值