前言
迭代器作为一个抽象概念,在程序设计中并没有直接对应于这个概念的实物。在设计模式中,iterator模式定义为:提供一种方法,能使之依序巡防某个容器所含元素,而无须暴露该容器的内部表达方式。
3.1 迭代器设计思维–STL关键所在
STL的核心思想:将容器和算法分离,彼此独立设计,然后用迭代器将两者撮合。
3.2 迭代器是一种智能指针
指针最常见的行为是derefence和member acess,因此迭代器编程的工作就是要对operator*和operator->进行重载。
3.3 迭代器所指的类型
如果在算法中要声明一个变量,其类型为迭代器所指的类型,应该如果做呢?
利用fuction template的参数推导机制
template<class I, class T>
void func_impl(I iter, T t)
{
T tmp; //T就是迭代器所指之物
//...这里做func()应该做的事情
}
template<I iter>
inline void func(I iter)
{
func_impl(iter, *iter); //func的全部工作迁移到func_impl
}
int main()
{
int i;
func(&i);
}
我们以func()为对外接口,实际操作放在func_impl中,由于其为一个function template,一旦被调用,编译器会自动进行参数推导,于是知道了类型T,解决问题。
Traits编程技法–STL源代码门钥
如果用于函数的返回值,上述方法不能使用。
声明内嵌类型
template <class T>
class MyIter{
public:
typedef T value_type;
T *ptr;
MyIter(T *p = 0):ptr(p){}
T& operator*()const{return *ptr;}
};
利用typedef T value_type把模版中的类型T暴露出来,我们可以通过以下的方法让函数的返回值为T的类型.
template <class I>
typename I::value_type
func(I iter)
{
return *iter;
}
其中模版I必须提高value_type变量的自定义迭代器,编译器在编译阶段,会通过MyIter的实际模板值推导出func函数的返回值类型应该是什么,这样func函数的返回值就可以随着我们自定义迭代器传入的模板参数的改变而改变。
整体实现代码为:
#include <iostream>
using namespace std;
template <class T>
class MyIter
{
public:
typedef T value_type;
T *ptr;
MyIter(T *p = NULL) : ptr(p){}
T& operator*() const
{
return *ptr;
}
};
template<class I>
typename I::value_type func(I iter)
{
return *iter;
}
int main()
{
MyIter<int> myIter(new int(1));
cout << func(myIter) << endl; //1
return 0
}
但是对于原生指针就会有问题,因为原生指针是没有内嵌类型的。利用模板的特化的特性,在中间增加一层Traits类,让我们设计的func函数既支持自定义的迭代器又支持通用指针。
首先设计一个class template专门来萃取迭代器的特性
template <class I>
struct iterator_traits
{
typedef typename I::value_type value_type;
};
这个traits的意义是,如果I定义有自己的value_type,那么通过这个traits的作用,萃取出来的value_type就是I::value_type。
偏特化解决原生指针和指向常数对象的原生指针
template <class T>
struct iterator_traits<T*>
{
typedef T value_type;
};
template <class T>
struct iterator_traits<const T*>
{
typedef T value_type;
};
整体的代码如下:
#include <iostream>
using namespace std;
//自定义迭代器
template<class T>
struct MyIter
{
typedef T value_type;
T *ptr;
MyIter(T* p = NULL):ptr(p)
{
}
T& operator*() const
{
return *ptr;
}
};
//Traits编程技法
//支持自定义迭代器
template<class T>
class Traits
{
public:
typedef typename T::value_type value_type;
};
//特化,支持传统通用指针
template <class T>
class Traits<T*>
{
public:
typedef T value_type;
};
//特化,支持传统的const指针
template<class T>
class Traits<const T*>
{
public:
typedef T value_type;
};
//模版函数
template <class I>
typename Traits<I>::value_type func(I iter)
{
return *iter;
}
//测试
int main(int argc, char** argv)
{
MyIter<int> p(new int(1));
const char* ptr = "abc";
int *a = new int(9);
cout << func(p) << endl;
cout << func(a) << endl;
cout << func(ptr) << endl;
return 0;
}
那么现在我们拥有三种版本:
1. 泛型版本
2. 原生指针
3. pointer-to-const
这样无论是迭代器 Iter 还是 原生指针 int* 还是 const int * 我们都可以通过traits取出正确的value_type。
最常用的迭代器类型有五种
1. value_type
迭代器所指对象的类型
2. difference_type
两个迭代器之间的距离。也可以用来表示一个容器的最大容量。如果泛型算法提供计数功能,就必须使用迭代器的这个型别做返回值。
3. reference_type
引用类型。从迭代器所指的内容是否允许改变来看,可以将迭代器分为两种:不允许改变所指之物的内容,称为constant iterator;可以改变所指之物的内容,称为 mutable iterator。他们对应的例子分别是 cosnt int * 和 int 。他们对应的例子分别是 cosnt int 和 int 。当我们对 mutable iterator (int ) 进行解引用操作时,获得的不应该是一个右值(rvalue),应该是一个左值(lvalue),因为右值不允许赋值操作(assignment),左值才允许.
4. pointer_type
指针类型
- iterator_category
迭代器类型
迭代器被分为以下五类:
只读,唯写,前向,双向,随机存取。
* Input Iterator:这种迭代器所指的对象,不允许外界改变;只读(Read Only)
* Output Iterator:这种迭代器所指的对象,只可以写入;唯写(Write Only)
* Forward Iterator:允许“写入型”算法在此种迭代器形成的区间上读写操作。
* Bidirectional Iterator:可双向移动。
* Random Access Iterator:涵盖了所有的算数能力,比如p+n,p-n,p[n],p1-p2,p1
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{ };
这里的 struct 只作为标记使用,所以不需要任何成员。
然后设计__advance()函数:
template<typename _InputIterator , typename _Distance>
inline void
__advance( _InputIterator& __i , _Distance __n , input_iterator_tag )
{
// concept requirements
glibcxx_function_requires( _InputIteratorConcept<_InputIterator> )
_GLIBCXX_DEBUG_ASSERT( __n >= 0 );
while(__n--)
++__i;
}
template<typename _BidirectionalIterator , typename _Distance>
inline void
__advance( _BidirectionalIterator& __i , _Distance __n ,
bidirectional_iterator_tag )
{
// concept requirements
__glibcxx_function_requires(
_BidirectionalIteratorConcept<_BidirectionalIterator> )
if(__n > 0)
while(__n--)
++__i;
else
while(__n++)
--__i;
}
template<typename _RandomAccessIterator , typename _Distance>
inline void
__advance( _RandomAccessIterator& __i , _Distance __n ,
random_access_iterator_tag )
{
// concept requirements
__glibcxx_function_requires(
_RandomAccessIteratorConcept <_RandomAccessIterator > )
__i += __n;
}
这里第三个参数只作为标记使用,函数内并没有使用它们。
然后接下来是 advance 函数:
template<typename _Iter>
inline typename iterator_traits<_Iter>::iterator_category
__iterator_category( const _Iter& )
{
return typename iterator_traits<_Iter>::iterator_category();
}
template<typename _InputIterator , typename _Distance>
inline void
advance( _InputIterator& __i , _Distance __n )
{
// concept requirements -- taken care of in __advance
typename iterator_traits<_InputIterator>::difference_type __d = __n;
std::__advance( __i , __d , std::__iterator_category( __i ) );
}