参考书籍:STL源码剖析(候捷)
STL/iterator
%1.STL的六大组件:
@1.containers:各种数据结构。
@2.algorithm: 各种算法。
@3.iterator: containers和algorithm之间的桥梁。
@4.functor: 行为类似函数,可作为算法的policy。
@5.adapters: 对containers,algorithm和iterator的封装,
例如,stack和queue是对底层deque的封装。
@6.allocator: 负责空间(不一定是内存)的分配和管理。
%2.设计模式--迭代器UML
%3.itrator:
@1.面临的问题:
各种数据在不同的容器中有不同的存储结构,
算法用于处理各种数据,关系的是数据本身,
如何做到数据的存储方式对算法来说是透明的??
@2.解决方案:迭代器模式
集合(容器)本身给客户(算法)提供一种遍历集合(容器)里所有元 素的方法,
同时避免暴露集合(容器)本身内部的实现。
@3.实现方案:
设计适当的相关类型(associated types),是迭代器的责任。
设计适当的迭代器,则是容器的责任。
迭代器是algorithm和containers之间的桥梁。
至于算法,完全可以独立于容器和迭代器之外自行发展,只要设计时以迭代器为对外接口就行。
@4.迭代器最重要的编程工作就是
对operator*(dereference)
和operator->(member access) 进行重载(overloading)。
%4.实现方案的具体问题及解决方法:
?1.迭代器由谁负责提供具体实现??
:不同的容器,数据存储方式不同,对数据的操作方式不同,
唯容器本身,才知道该设计出怎样的迭代器来走访自己,
并执行迭代器该有的各种行为(前进、后退、取值、取用成员…)。
?2.对于用户(算法)来说,如何获取传进来的iterator所指的Iterm的 相关类型??
:利用template函数的型参类型自推导功能可以获得iterator所指 Iterm的类型。eg:
int main()
{
int i;
func(&i); //传进iterator
}
template <class I>
inline
void func(Iiter)
{
func_impl(iter,*iter);
}
template <class I, class T>
void func_impl(I iter, T t) //当该函数被调用时,编译器会自动对 template变量进行推导。
//利用传进来的*iter获得其类型T,达到 目的
{
T tmp; // 这里解决了问题。T 就是迭代器所指Iterm的类型,本 例为int
// ... 这里做原本func() 应该做的全部工作
};
?3.对问题2的改善。
注意到?2中func函数是通过其子函数区获得iterator所指Iterm的 类型的,如果func函数本身要用到Iterm类型呢?
有没有主要到?2中func函数返回值是void类型,
如果算法函数想返回iterator所指Iterm相关类型时,怎么办??
template函数的型参类型自推导功能只能推导型参类型,
但这只是该Iterm相关类型的其中一种,
但在其他情况下,例如无法推导函数返回值类型??想获得其他相关类 型怎么办??
:Iterm相关类型由iterator自己定义,用户(算法)直接提取其类型。 即使用nested type(内嵌类型)编程方法,eg:
template <class T>
struct ConcreteIterator
{
typedef T value_type; // 内嵌类型定义(nested type)
T* ptr;
ConcreteIterator(T* p=0) : ptr(p) { }
T& operator*() const { return *ptr; }
// ...
};
template <class I>
typename I::value_type // 这一整行是func 的回返值型 别,value_type为ConcreteIterator中定义的内嵌类型
func(I ite)
{ return *ite; }
// ...
}
ConcreteIterator<int> ite(new int(8));
cout <<func(ite);
注意,func() 的回返型别必须加上关键词typename,因为T 是一个template 参
数,在它被编译器具体化(调用)之前,编译器对T一无所知,
换句话说编译器此时并不知道ConcreteIterator<T>::value_type 代表的是一个类型
或是一个member function 或是一个data member。
关键词typename 的用意在告诉编译器说这是一个类型,如此才能顺利通过编译。
?4.上述nested type的实现方法,
前提是iterator是class type(concreteIterator是类对象,可以在 类内定义nested type),
但对于int* , double*等原生指针怎么处理??
:template泛化编程可以解决类抽象问题,
当需要对类抽象中的特定类做特殊处理时,可以用template partial specialization。
@1.针对(任何)template 参数更进一步的条件限制,所设计出来的 一个特化版本。
eg:
template<typename T>
class C { ... }; // 这个泛化版本允许(接受)T 为任何类型
//我们便很容易实现它有一个形式如下的partial //specialization:
template<typename T>
class C<T*> { ...};//这个特化版本仅适用于「T 为原生指针」的情况
//T 为原生指标」便是「T 为任何型别」的一个更 //进一步的条件限制
为了简化客户编程,可以将iterator为class type和原生指针两种情 况用class iterator_triats进行封装,用户使用该class提供的同一 接口即可获得不同iterator的特性。
下面这个class template 专门用来「萃取」迭代器的特性,而value type 正是迭代器所指Iterm的类型:
情况一:对class type的iterator进行trait:
template <class I>
struct iterator_traits{ // traits 意为「特性」
typedef typename I::value_type value_type;
};
这个所谓的traits,其意义是,如果I(传进来的iterator)定义有自 己的value type,
那么透过这个traits 的作用,萃取出来的value_type 就是 I::value_type。
先前那个func() 可以改写成这样:
template <class I>
typenameiterator_traits<I>::value_type//这一行是函数返回值类型
func(I ite)
{ return *ite; }
情况二:对原生指针的iterator进行trait:
//template partial specializations
template <class T>
struct iterator_traits<T*> { // 偏特化版— 迭代器是个原生指标
typedef T value_type;
};
注意:当iterator是const T* 时,获得的value_type为const T类 型,这对客户来说,使用或返回一个无法赋值(const 常量)的临时变 量是没有意义的。
所以应该增加一个template partial specialization版本,将const 改为non-const,如下:
template <classT>
struct iterator_traits<const T*> { //偏特化版— 当迭代器是个 //pointer-to-const
Typedef T value_type; //萃取出来的型别应该是T 而非const T
};
小结traits:
traits 编程技法,大量运用于STL 实作品中。它利用nested type(内 嵌类型)的编程技巧与编译器的template 自变量推导功能,补强C++ 未 能提供的关于类型识别方面的能力。
?5.iterator的相关类型(associated type)究竟有哪些??
:
@1.迭代器相应型别之一:value type
所谓value type,是指迭代器所指对象的类型
@2.迭代器相应型别之二: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) {
typename iterator_traits<I>::difference_type n = 0;
for ( ; first != last; ++first)
if (*first == value)
++n;
return n;
}
针对相应型别difference type,traits 的两个(针对原生指标而 写的)特化版本如下,以C++ 内建的ptrdiff_t(定义于<cstddef> 表头档)做为原生指标的difference type
@3.迭代器相应型别之三:reference type
从「迭代器所指Iterm的内容是否可修改」的角度看,
迭代器分为两种:
##1.不允许改变「所指对象内容」,称为constant iterators,
例如const int* pic;
##2.允许改变「所指对象内容」,称为mutable iterators,
例如int* pi。
当我们对一个mutable iterators 做dereference(*)动作时,获 得的不应该是个右值(rvalue),应该是个左值(lvalue),因为右 值不允许赋值动作(assignment)。
在C++ 中,函式如果要传回左值,都是以by reference的方式进行,
%%1.当p 是个mutable iterators 时,如果其value type 是T, 那么*p 的型别不应该是T,而是T&。
%%2.当p是个constant iterators,其value type 是T,那么*p 的 型别不应该是const T,而应该是const T&。这里所讨论的*p 的 型别,即所谓的reference type
@4. 迭代器相应型别之四:pointer type
回传一个指针p,既可以获得p所指的地址,
也可以获得p所指地址上的内容(*p)。
@5. 迭代器相应型别之五:iterator_category
根据移动特性与施行动作,迭代器被分为五类:
Input Iterator:这种迭代器所指对象,不允许外界改变,read only。
Output Iterator:只写(write only)。
Forward Iterator:可读可写,算法(例如replace())
Bidirectional Iterator:可双向移动。
Random Access Iterator:前四种迭代器都只供应部份指针算术能 力(前三种支持operator++,第四种再加上operator--),
第五种则涵盖所有指针算术能力,包括p+/-n, p[n], p1-p2, p1<p2。