迭代器(iterators)
iterator
模式定义如下:提供一种方法,使之能够依序寻访某个容器所含的各个元素,有无需暴露该容器的内部表述方式。
1. 迭代器的自增
以 List 为例,迭代器重载了自增运算符来实现指针的右移,以下为前置及后置递增的实现方法:
// 前置自增,因为返回的是右移之后的结果,所以要返回一个引用。
ListIter& operator++(){
ptr = ptr->next();
return *this;
}
// 后置递增,返回的是当前的指针,这个tmp用完就释放了,不用返回引用。
ListIter operator++(int){
ListIter tmp = *this;
++*this;
return tmp;
}
2. Traits编程技法
在迭代器的使用过程中,常常会用到迭代(比如所指对象)的型别,但是CPP并没有typeof()
的操作,所以需要一些其他的方式,解决办法如下:
-
首先是利用模板函数的参数推导机制:
template <class I, class T> void func_impl(I iter, T t){ T tmp; // 这里就获得了类型 T // ..... }
-
但如果这个
value type
适用于函数的传回值,那就没辙了。因为template
只能用于推导参数型别,而无法用于推导函数返回值型别。因此然后尝试使用声明内嵌型别:template <class T> struct MyIter{ typedef T value_type; T* ptr; MyIter(T* p=0):ptr(p){} T& operator*() const{return *ptr;} // .... } template <class I> typename I::value_type // 返回值型别 func(I ite){ return *ite; } // ... MyIter<int> ite(new int(8)); cout<< func(ite); // 8
但是这也有一个问题,那就是并不是所有的迭代器都不是
class type
,比如原生指针,这样就无法为其定义内嵌类型。 -
模板偏特化
模板偏特化是针对
template
更进一步的条件限制设计出的特化版本。可以针对不是class type
的迭代器,为其创建一些之,来取出正确的(希望的)value type
。最常用的迭代器相应型别有五种:value type
,difference type
,pointer
,reference
,iterator catagoly
。对于所有的迭代器,都要定义这五种型别,然后traits
就可以从中取出相应的值。value type
,即迭代器所指对象的型别difference type
,表示两个迭代器之间的距离,因此它也可以用来表示一个容器的最大容量。reference
,一个迭代器解引用(*p)后的型别,没被用过pointer
,指向迭代器所指的对象,没被用过iterator_categoly
,迭代器被分为五类:Input Iterator
,Output Iterator
,Forward Iterator
,Bidirectional Iterator
,Random Access Iterator
,前三种支持operator++
,第四种支持operator--
,第五种则包含所有算术能力:双向、跳跃等。这五种迭代器的阶级依次递增。- 对于任何一个迭代器,其类型永远应该落在“该迭代器所隶属之各种类型中,最强化的那个”。例如,
int*
既是Random Access Iterator
,又是Bidirectional lterator
,同时也是Forward lterator
,而且也是Input lterator
,那么,其类型应该归属为random_access_iterator_tag
。 - 而算法在接受一个迭代器类型的时候,以能接受的最低阶迭代器类型命名。
- 设计适当的相应型别( associated types),是迭代器的责任。设计适当的迭代器,则 是容器的责任。唯容器本身,才知道该设计出怎样的迭代器来遍历自己,并执行迭代器该有的各种行为(前进、后退、取值、取用成员…)。
3. 针对不同迭代器的 traits
template <class Iterator>struct iterator_traits {
typedef typename Iterator::iterator_category iterator_category ;
typedef typename Iterator::valuetype value_type;
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
};
//针对原生指针(native pointer)而设计的 traits 偏特化版
template <class T>
struct iterator_traits<T*>{
typedef random_access_iterator_tag iterator_category ;
typedef T value_type ;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
};
//针对原生之pointer-to-const而设计的 traits 偏特化版
template <class T>
struct iterator_traits<const T*>{
typedef random_access_iterator__tag iterator_category ;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef constT* pointer;
typedef const T& reference;
};
4. __type_traits
traits
编程技法很棒,适度弥补了C++语言本身的不足。STL只对迭代器加以规范,制定出iterator_traits
这样的东西。SGI 把这种技法进一步扩大到迭代器以外的世界,于是有了所谓的__type_traits
。双底线前缀词意指这是SGISTL内部所用的东西,不在STL标准规范之内。
iterator_traits
负责萃取迭代器的特性,__type_traits
则负责萃取型别的特性。此处我们所关注的型别特性是指:这个型别是否具备non-trivial defalt ctor
?是否具备non-trivial copy ctor
?是否具备non-trivial assignment operator
?是否具备non-trivial dtor
?如果答案是否定的,我们在对这个型别进行构造、析构、拷贝、赋值等操作时,就可以采用最有效率的措施(例如根本不调用身居高位,不谋实事的那些constructor,destructor
),而采用内存直接处理操作如malloc()、memcpy ()
等等,获得最高效率。这对于大规模而操作频繁的容器,有着显著的效率提升。
程序可以利用如下代码来回带上面的问题:
__type_traits<T>::has_trivial_default_constructor
__type_traits<T>::has_trivial_copy_constructor
__type_traits<T>::has_trivial_assignment_operator
__type_traits<T>::has_trivial_destruct
__type_traits<T>::is_POD_type
而上述式子则传回如下的东西进行回复:
struct __true_type {};
struct __false_type {};
这两个空白 classes没有任何成员,不会带来额外负担,却又能够标示真假,满足我们所需。
SGI 默认把所有内嵌性别都定义为__false_type
,然后针对每一个标量型别设计适当的__false_type
特化版本。具体见第106页。