一、迭代器介绍
迭代器(Iterator),是一种抽象的概念,在设计模式中也有这种设计模式,它提供了一种方式,能够依次访问容器的相关元素,并且不需要暴露容器内部的相关细节。说得直白一些,就是提供了一套访问所有容器的相关的抽象。它提供了访问这些容器的最小的公共接口,将算法和容器进行了解耦,并利用迭代器将其二者再进行合理的粘合。
迭代器的比较粗浅的理解方式是把它当作一个普通的指针,而指针一般可以进行++和–操作。从而达到对容器内部元素的逐一访问。但实际上,迭代器比之要复杂许多,因为它是抽象出来,普适几乎所有的容器的一种数据结构。在STL中,迭代器主要为分五类:
1、输入迭代器(Input Iterator):迭代器指向的对象为只读,只支持自增运算符。
2、输出迭代器(Ouput Iterator): 迭代器指向的对象为只写,只支持自增运算符。
3、前向迭代器(Forward Iterator): 支持读和写,只支持自增运算符。
4、双向迭代器(Bidirectional Iterator) : 支持读和写,支持自增和自减运算符。
5、随机迭代器(Random Access Iterator) : 支持读和写,支持所有指针的算术运算。
它们之间的关系如下:
需要说明的是,这张图是引自侯捷先生的《STL源码剖析》中的3.4.5节中的图片,这张图片表现的关系并不是传统意义上的c++中的继承关系,而是concept(概念)和 refinement(强化)的关系。
在STL中应用到了前面提到过很多次的traits技术(萃取)。它可以动态的对迭代的容器进行判断并自动推导相关的操作。
在迭代器的设计中,还引入了相关的辅助函数,如:
1、advance(p, n):将迭代器 p 向前或向后移动 n 个位置。
2、distance(p, q):计算迭代器p,q的距离。注意,p,q的位置不能置反,否则会进入死循环。
3、iter_swap(p, q):交换迭代器 p、q 各自指向的值。
另外,迭代器也有适配器:
1、inserter(c,p), 在容器c的p点前插入,并返回p。
2、back_inserter©, 创建一个迭代器并在c的末尾添加指定数据, 支持push_back。
3、front_inserter©, 创建一个迭代器并在c的头部添加指定元素, 支持push_front。
4、make_move_iterator§, 移动迭代器, p是输入迭代器。
二、源码分析
看一下std::vector的迭代器源码定义为:
template<class _Myvec>
class _Vector_iterator
: public _Vector_const_iterator<_Myvec>
{ // iterator for mutable vector
public:
using _Mybase = _Vector_const_iterator<_Myvec>;
using iterator_category = random_access_iterator_tag;
using value_type = typename _Myvec::value_type;
using difference_type = typename _Myvec::difference_type;
using pointer = typename _Myvec::pointer;
using reference = value_type&;
_Vector_iterator()
{ // construct with null vector pointer
}
_Vector_iterator(pointer _Parg, const _Container_base *_Pvector)
: _Mybase(_Parg, _Pvector)
{ // construct with pointer _Parg
}
_NODISCARD reference operator*() const
{ // return designated object
return (const_cast<reference>(_Mybase::operator*()));
}
_NODISCARD pointer operator->() const
{ // return pointer to class object
return (_Const_cast(_Mybase::operator->()));
}
_Vector_iterator& operator++()
{ // preincrement
++*(_Mybase *)this;
return (*this);
}
_Vector_iterator operator++(int)
{ // postincrement
_Vector_iterator _Tmp = *this;
++*this;
return (_Tmp);
}
_Vector_iterator& operator--()
{ // predecrement
--*(_Mybase *)this;
return (*this);
}
_Vector_iterator operator--(int)
{ // postdecrement
_Vector_iterator _Tmp = *this;
--*this;
return (_Tmp);
}
_Vector_iterator& operator+=(const difference_type _Off)
{ // increment by integer
*(_Mybase *)this += _Off;
return (*this);
}
_NODISCARD _Vector_iterator operator+(const difference_type _Off) const
{ // return this + integer
_Vector_iterator _Tmp = *this;
return (_Tmp += _Off);
}
_Vector_iterator& operator-=(const difference_type _Off)
{ // decrement by integer
return (*this += -_Off);
}
_NODISCARD _Vector_iterator operator-(const difference_type _Off) const
{ // return this - integer
_Vector_iterator _Tmp = *this;
return (_Tmp -= _Off);
}
_NODISCARD difference_type operator-(const _Mybase& _Right) const
{ // return difference of iterators
return (*(_Mybase *)this - _Right);
}
_NODISCARD reference operator[](const difference_type _Off) const
{ // subscript
return (*(*this + _Off));
}
_NODISCARD pointer _Unwrapped() const
{
return (this->_Ptr);
}
};
把一些预定义和宏去除,你会发现,是不是就是简单的指针操作?源码之前,了无秘密!
三、例程
迭代器的例子好多,先看一下STL库的std::advance()和std::distance()这两个函数:
template<class _BidIt,
class _Diff>
_CONSTEXPR17 void _Advance1(_BidIt& _Where, _Diff _Off, bidirectional_iterator_tag)
{ // increment iterator by offset, bidirectional iterators
for (; 0 < _Off; --_Off)
{
++_Where;
}
// the following warning is triggered if _Diff is unsigned
#pragma warning(suppress: 6294) // Ill-defined for-loop: initial condition does not satisfy test.
// Loop body not executed.
for (; _Off < 0; ++_Off)
{
--_Where;
}
}
template<class InputIterator>
inline typename std::iterator_traits<InputIterator>::difference_type distance(InputIterator first, InputIterator last){
return __distance(first, last, std::iterator_traits<InputIterator>::iterator_category());
}
template<class InputIterator>
inline typename std::iterator_traits<InputIterator>::difference_type __distance(InputIterator first, InputIterator last, std::input_iterator_tag){
std::iterator_traits<InputIterator>::difference_type n = 0;
while (first != last){
++first; ++n;
}
return n;
}
template<class InputIterator>
inline typename std::iterator_traits<InputIterator>::difference_type \
__distance(InputIterator first, InputIterator last, std::random_access_iterator_tag){
return last - first;
}
就是单纯的指针的增加和减少。
再看一个实际应用的例子:
#include <iostream>
#include <iterator>
#include <vector>
int main()
{
std::vector<int> v{ 3, 1, 4 };
auto vi = v.begin();
std::advance(vi, 2);
std::cout << *vi << ' ';
vi = v.end();
std::advance(vi, -2);
std::cout << *vi << '\n';
}
输出结果:
4 1
四、总结
STL中的迭代器,更象是一种工具或者说桥梁,它被抽象出来控制具体的容器的相关元素访问,达到一种最小侵入性的访问接口。这对设计人员来讲,是非常有意义的。通过这咱思想,可以借鉴为自己的工程所用。这也正合了计算机设计中的分层的概念,如果把容器、算法和其它相关操作理解成层的概念的话,为了方便层之间的访问,就增加了迭代器这一层。