1. 迭代器
迭代器是算法(业务逻辑)和容器之间的黏合剂。
迭代器关键是重载operator*和operator->,前者可以作为左值,后者返回指针;前者的语意是取得迭代器指向的对象,后者的语意是取得迭代器指向对象的地址。
单向链表,重载operator++()和operator++(int),前者可以作为左值,后者不能作为左值。
1.1. function template的参数推导(argument deducation)
function template的参数推导(argument deducation)机制?
似乎hp版的STL并未使用参数推导机制?
function templatede参数推导机制无法解决推导函数的返回值型别。
1.2. Traits编程技法
迭代器所指对象的类型,称为该迭代器的value type。
采用内嵌类型来解决问题(前提是迭代器是class type)。
但不是所有迭代器都是class type,例如原生指针(如int * p)。
1.3. 采用偏特化和增加中间层来解决原生指针问题
增加的中间层如下:
template<class Iter> // 模型形参Iter为一个迭代器
struct iterator_traits
{
typedef typename Iter::value_type value_type; // 当迭代器为非原生指针采用
// 其内嵌类型value_type
};
似乎hp版并未使用偏特化?
文中提到的指针偏特化似乎VC6.0编译器不支持?
1.4. value type
value type迭代器所指对象类型,iterator内嵌类型之一。任何一个迭代器必须定义自己的value type内嵌类型。
1.5. difference type
difference type表示两个迭代器之间距离变量的类型,iterator内嵌类型之一。
例如vector的定义:
template<class _Ty, class _A = allocator<_Ty> >
class vector {
public:
……
typedef _A::difference_type difference_type; // difference type采用其分配器
// 的difference type
……
};
template<class _Ty>
class allocator {
public:
……
typedef _PDFT difference_type; // _PDFT就是int
……
};
在vector自己的迭代器中使用:
class const_iterator : public _Ranit<_Bool, difference_type> {
public:
……
// 计算两个迭代器距离
difference_type operator-(const const_iterator _X) const
{return (_VBITS * (_Ptr - _X._Ptr)
+ (difference_type)_Off
- (difference_type)_X._Off); }
……
};
1.6. reference type
根据迭代器所指对象是否可以改变,可将迭代器分为两种:constant iterators和mutable iterators。
reference type指迭代器所指对象的引用类型,即传回一个左值表示迭代器所指之对象,iterator内嵌类型之一。同样也分为constant和non-constant。
例如:
template<class _Ty, class _A = allocator<_Ty> >
class vector {
public:
……
typedef _A::reference reference;
typedef _A::const_reference const_reference;
……
const_reference at(size_type _P) const
{if (size() <= _P)
_Xran();
return (*(begin() + _P)); }
reference at(size_type _P)
{if (size() <= _P)
_Xran();
return (*(begin() + _P)); }
const_reference operator[](size_type _P) const
{return (*(begin() + _P)); }
reference operator[](size_type _P)
{return (*(begin() + _P)); }
……
};
1.7. pointer type
pointer type之迭代器所指对象的指针类型,即传回一个左值表示迭代器所指之对象的地址。
例如:
template<class _Ty, class _A = allocator<_Ty> >
class list {
protected:
……
typedef _A::pointer _Tptr;
typedef _A::const_pointer _Ctptr;
……
class const_iterator : public _Bidit<_Ty, difference_type> {
public:
……
_Ctptr operator->() const
{return (&**this); }
……
};
……
class iterator : public const_iterator {
public:
……
_Tptr operator->() const
{return (&**this); }
……
};
};
1.8. iterator_category
iterator_category指迭代器类型变量之类型(有点拗口,呵呵),iterator内嵌类型之一。
迭代器分为5类:
1) input iterator:迭代器所指对象只读;
2) output iterator:迭代器所指对象只写;
3) forward iterator:前向迭代器,所指对象可读写;
4) bidirectional iterator:双向迭代器,所指对象可读写;
5) random access iterator:随机访问迭代器,所指对象可读写。
其从属关系如下图:
input iterator |
output iterator |
forward iterator |
bidirectional iterator |
random access iterator |
图 迭代器分类与从属关系
其中箭头代表的并非继承而是概念(concept)和强化(refinement)关系。
效率是研究STL过程中的一个重要课题,也是建立一个函数库的重要考虑的方面。假使有个算法可接受forward iterator,你用random access iterator喂给它也行,但不代表最佳!记住够用就好!!
正是基于效率才有以下advance()改进:
advance函数有两个形参:p和n,函数将迭代器累进n次。
基于效率考虑,advance调用三个实现细节:advance_II、advance_BI、advance_RAI,分别用来实现input iterator、bidirectional iterator和random access iterator三种迭代器累进n。三个实现细节如下:
template<class InputIterator, class Distance >
void advance_II(InputIterator & i, Distance n)
{
while(n--) // 假设n大于0
i++;
}
template<class BidirectionalIterator, class Distance >
void advance_II(BidirectionalIterator & i, Distance n)
{
if(n >= 0)
{
while(n--)
i++;
}
else
{
while(n++)
i--;
}
}
template<class RandomAccessIterator, class Distance >
void advance_II(RandomAccessIterator & i, Distance n)
{
i += n;
}
然后,advance函数根据需要调用相应的实现,如下:
template< class InputIterator, class Distance >
void advance(InputIterator & i, Distance n)
{
if(is_random_access_iterator(i))
{
advance_RAI(i, n);
}
else if(is_bidirectional_iterator(i))
{
advance_BI(i, n);
}
else
{
advance_II(i, n);
}
}
这里存在一个效率问题,这种分支结构是执行期决定使用哪个版本,能否在编译期决定采用哪个版本呢?
采用重载的方式解决这个问题!
定义三个实现重载:
template<class InputIterator, class Distance>
inline void __advance(InputIterator & i, Distance n, input_iterator_tag)
{
while (n--)
{
++i;
}
}
template<class BidirectionalIterator, class Distance>
inline void __advance(InputIterator & i, Distance n, bidirectional_iterator_tag)
{
if(n >= 0)
{
while (n--)
{
++i;
}
}
else
{
while (n++)
{
--i;
}
}
}
template<class RandomAccessIterator, class Distance>
inline void __advance(RandomAccessIterator & i, Distance n, random_access_iterator_tag)
{
i += n;
}
input_iterator_tag、bidirectional_iterator_tag和random_access_iterator_tag是重载的关键,分别是三个结构,其定义如下:
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 {};
结构的继承关系描述了5种迭代器类型的从属关系。为了使效率最高,结构不包含任何成员,即是一个空结构;同时,__advance形参中只定义其类型,并未指定其名称,标明其只是用来激活重载机制。
然后advance函数的调用采用traits机制和重载机制完成针对不同需求调用不同的实现的要求:
template<class InputIterator, class Distance>
inline void advance(InputIterator & i, Distance n)
{
__advance(i, n, iterator_traits<InputIterator>::iterator_category());
}
这里之所以采用InputIterator,是因为STL的一个命名规则:以算法所能接受之最低阶迭代器类型,来为其迭代器类型参数命名。
1.9. 消除“单纯传递调用的函数”
这里之所以采用InputIterator,是因为STL的一个命名规则:以算法所能接受之最低阶迭代器类型,来为其迭代器类型参数命名。
什么叫“单纯传递调用的函数”呢?例如__advance的forward iterator版:
template<class ForwardIterator, class Distance>
inline void __advance(ForwardIterator & i, Distance n, forward_iterator_tag)
{
while (n--)
{
++i;
}
}
和__advance的input iterator版相比,只是模板的第一个形参和函数最后一个形参不同,其实现部分完全相同,该函数仅仅是为了重载时传递调用的函数。
而调用重载函数进行参数匹配时的继承机制可以消除这一点,即当参数不匹配时,根据实参类型继承树向上搜索,其算法(单继承,多重继承类似)描述如下:
1) 以实参类型为被匹配类型;
2) 判断被匹配类型是否与某个重载函数形参相匹配,匹配进入4;
3) 以被匹配类型的基类作为新的被匹配类型,返回2;若被匹配类型无基类,进入5;
4) 调用该函数;
5) 编译器报错。
由于forward_iterator_tag派生至input_iterator_tag,因此可以省略单纯传递调用的函数__advance的forward iterator版,这也正是采用继承机制实现5种迭代器类型的重要原因!
1.10. std::iterator的保证
为了避免STL库开发者开发的容器中迭代器不满足要求,STL提供一个迭代器基类如下:
template<class Category,
class T,
class Distance = ptrdiff_t,
class Pointer = T*, /* 实际上hp版的STL并不包含该项 */
class Reference = T&/* 实际上hp版的STL并不包含该项 */>
struct iterator {
typedef Category iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef Pointer pointer; /* 实际上hp版的STL并不包含该项 */
typedef Reference reference; /* 实际上hp版的STL并不包含该项 */
};
从hp版STL看,hp版STL认为Pointer type和Reference type并不是iterator必备特性之一?