跟我学C++中级篇——STL的迭代器

200 篇文章 27 订阅

一、迭代器介绍

迭代器(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中的迭代器,更象是一种工具或者说桥梁,它被抽象出来控制具体的容器的相关元素访问,达到一种最小侵入性的访问接口。这对设计人员来讲,是非常有意义的。通过这咱思想,可以借鉴为自己的工程所用。这也正合了计算机设计中的分层的概念,如果把容器、算法和其它相关操作理解成层的概念的话,为了方便层之间的访问,就增加了迭代器这一层。
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值