侯捷的这一章写的是iterators与traits编程技法,之前就是对于traits这个技法不是很懂,花了一段时间去了解,所以看这一节的时候,主要是关注iterator的实现技术了。
迭按照设计模式给的iterator的定义,迭代器提供一种方法,使之能够依序巡防某个聚合物(容器)所含的各个元素,而又无需暴露该聚合物内部的表达方式。 STL将数据容器(container) 与算法(algorithm)分开,彼此设计独立,最后通过iterator实现两者的胶合。
1.auto_ptr智能指针
在STL iterator之前,先看一下c++自带的auto_ptr,源码如下。
template<class _Tp1> struct auto_ptr_ref {
_Tp1* _M_ptr;
auto_ptr_ref(_Tp1* __p) : _M_ptr(__p) {}
};
template <class _Tp> class auto_ptr {
private:
_Tp* _M_ptr;
public:
typedef _Tp element_type;
explicit auto_ptr(_Tp* __p = 0) __STL_NOTHROW : _M_ptr(__p) {}
auto_ptr(auto_ptr& __a) __STL_NOTHROW : _M_ptr(__a.release()) {}
#ifdef __STL_MEMBER_TEMPLATES
template <class _Tp1> auto_ptr(auto_ptr<_Tp1>& __a) __STL_NOTHROW
: _M_ptr(__a.release()) {}
#endif /* __STL_MEMBER_TEMPLATES */
auto_ptr& operator=(auto_ptr& __a) __STL_NOTHROW {
if (&__a != this) {
delete _M_ptr;
_M_ptr = __a.release();
}
return *this;
}
#ifdef __STL_MEMBER_TEMPLATES
template <class _Tp1>
auto_ptr& operator=(auto_ptr<_Tp1>& __a) __STL_NOTHROW {
if (__a.get() != this->get()) {
delete _M_ptr;
_M_ptr = __a.release();//首先释放__a的控制权,然后赋值
}
return *this;
}
#endif /* __STL_MEMBER_TEMPLATES */
// Note: The C++ standard says there is supposed to be an empty throw
// specification here, but omitting it is standard conforming. Its
// presence can be detected only if _Tp::~_Tp() throws, but (17.4.3.6/2)
// this is prohibited.
~auto_ptr() { delete _M_ptr; }
_Tp& operator*() const __STL_NOTHROW {
return *_M_ptr;
}
_Tp* operator->() const __STL_NOTHROW {
return _M_ptr;
}
_Tp* get() const __STL_NOTHROW {
return _M_ptr;
}
_Tp* release() __STL_NOTHROW {
_Tp* __tmp = _M_ptr;
_M_ptr = 0;
return __tmp;
}
void reset(_Tp* __p = 0) __STL_NOTHROW {
if (__p != _M_ptr) {
delete _M_ptr;
_M_ptr = __p;
}
}
// According to the C++ standard, these conversions are required. Most
// present-day compilers, however, do not enforce that requirement---and,
// in fact, most present-day compilers do not support the language
// features that these conversions rely on.
#if defined(__SGI_STL_USE_AUTO_PTR_CONVERSIONS) && \
defined(__STL_MEMBER_TEMPLATES)
public:
auto_ptr(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW
: _M_ptr(__ref._M_ptr) {}
auto_ptr& operator=(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW {
if (__ref._M_ptr != this->get()) {
delete _M_ptr;
_M_ptr = __ref._M_ptr;
}
return *this;
}
template <class _Tp1> operator auto_ptr_ref<_Tp1>() __STL_NOTHROW
{ return auto_ptr_ref<_Tp1>(this->release()); }
template <class _Tp1> operator auto_ptr<_Tp1>() __STL_NOTHROW
{ return auto_ptr<_Tp1>(this->release()); }
#endif /* auto ptr conversions && member templates */
};
可以看到,auto_ptr在构造的时候会获得对象的控制权,同时这种控制是独占,也就是说如果存在赋值的情况,那么会首先释放控制权,然后再移交控制权。
2.Iterator
STL定义了五种迭代器的类型:
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 {};
STL里面对于iterator的定义如下:
template <class _Category, class _Tp, class _Distance = ptrdiff_t,
class _Pointer = _Tp*, class _Reference = _Tp&>
struct iterator {
typedef _Category iterator_category;
typedef _Tp value_type;
typedef _Distance difference_type;
typedef _Pointer pointer;
typedef _Reference reference;
};
这个template里面,我们需要引入的参数只有两个,_category和_Tp,为了维持统一的traits接口,自己在定义iterator的时候,最好是继承这个iterator,这样就能够比较好的兼容STL的其他函数和容器。
三个版本的iterator_traits技术,分别提取T、T*、const T的属性。
template <class _Iterator>
struct iterator_traits {
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
};
//针对原生指针实现的偏特化
template <class _Tp>
struct iterator_traits<_Tp*> {
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef _Tp& reference;
};
//针对原生const pointer的偏特化
template <class _Tp>
struct iterator_traits<const _Tp*> {
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef const _Tp* pointer;
typedef const _Tp& reference;
};
template <class _Iter>
inline typename iterator_traits<_Iter>::iterator_category
__iterator_category(const _Iter&)
{
typedef typename iterator_traits<_Iter>::iterator_category _Category;
return _Category();//返回一个<span style="font-family: Arial, Helvetica, sans-serif;">iterator_traits<_Iter>::iterator_category对象,用于实现traits技术</span>
}
这个函数能够很容易的获得某个迭代器的distance类型,distance是指两个指针之间的距离。
template <class _Iter>
inline typename iterator_traits<_Iter>::difference_type*
__distance_type(const _Iter&)
{
return static_cast<typename iterator_traits<_Iter>::difference_type*>(0);
}
这个函数用于获得某个迭代器的value type
template <class _Iter>
inline typename iterator_traits<_Iter>::value_type*
value_type(const _Iter& __i) { return __value_type(__i); }
下面的__value_type函数和__distance_type函数返回的都是指针,很好奇这样做的原因,因为这几个函数一直没有被使用到,所以目前还不知道为什么一定要返回*。
然后就是advance函数了,advance函数主要是移动指针,有三个类型,主要是针对双向iterator、单向iterator以及random iterator。
template <class _InputIter, class _Distance>
inline void __advance(_InputIter& __i, _Distance __n, input_iterator_tag) {
while (__n--) ++__i;
}
双向iterator:
template <class _BidirectionalIterator, class _Distance>
inline void __advance(_BidirectionalIterator& __i, _Distance __n,
bidirectional_iterator_tag) {
__STL_REQUIRES(_BidirectionalIterator, _BidirectionalIterator);
if (__n >= 0)
while (__n--) ++__i;
else
while (__n++) --__i;
}
random iterator:
template <class _RandomAccessIterator, class _Distance>
inline void __advance(_RandomAccessIterator& __i, _Distance __n,
random_access_iterator_tag) {
__STL_REQUIRES(_RandomAccessIterator, _RandomAccessIterator);
__i += __n;
}
最后,进行统一的封装,利用traits技术,实现性能的优化。
template <class _InputIterator, class _Distance>
inline void advance(_InputIterator& __i, _Distance __n) {
__STL_REQUIRES(_InputIterator, _InputIterator);
__advance(__i, __n, iterator_category(__i));
}
侯捷这部分的内容主要是讲traits技术,源代码里面我看的主要是iterator_base里面的代码,还没有看reverse_iterator各种iterator,看到了再接着补充