# 迭代器的基本概念

迭代器(iterator)模式又称为游标(Cursor)模式,用于提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。

或者这样说可能更容易理解:Iterator 模式是运用于聚合对象的一种模式,通过运用该模式,使得我们可以在不知道对象内部表示的情况下,按照一定顺序(由 iterator 提供的方法)访问聚合对象中的各个元素。

# 迭代器产生原因

Iterator 类的访问方式就是把不同集合类的访问逻辑抽象出来,使得不用暴露集合内部的结构而达到循环遍历集合的效果。

# 迭代器的类型

输入迭代器 (InputIterator)、输出迭代器 (OutputIterator)、前向迭代器 (ForwardIterator)、双向迭代器 (BidirectionalIterator)、随机访问迭代器 (RandomAccessIterator)。

每个迭代器类型对应的操作如下:

类别 输出 (output) 输入 (input) 前向 (Forward) 双向 (Bidirection) 随机 (Random)
读 (read) =*p = *p =*p =*p
访问 (access) -> -> -> ->[]
写 (write) *p= *p= *p= *p=
选代 (select) ++ ++ ++ ++ / -- ++ / -- / + / - / += / --
比较 (compare) == / != == / != == / != / < / > / <= / > = == / != / < / > / <= / >=

五种迭代器之间的关系图:

# 为什么定义这么多迭代器

物尽其用,使得具体的操作使用具体类型的迭代器,避免迭代器的功能太大或者太小,导致使用起来不方便。

每个容器及其对应的迭代器的类型图表如下:

容器类型 类内迭代器类别
vector 随机访问迭代器
deque 随机访问迭代器
list 双向迭代器
set 双向迭代器
multiset 双向迭代器
map 双向迭代器
multimap 双向迭代器
unordered_set 前向迭代器
unordered_multiset 前向迭代器
unordered_map 前向迭代器
unordered_multimap 前向迭代器

# 流迭代器

流迭代器是特殊的迭代器,可以将输入 / 输出流作为容器看待(因为输入输出都有缓冲区的概念)。因此,任何接受迭代器参数的算法都可以和流一起工作。

# ostream_iterator 的使用

/**
   *  @brief  Provides output iterator semantics for streams.
   *
   *  This class provides an iterator to write to an ostream.  The type Tp is
   *  the only type written by this iterator and there must be an
   *  operator<<(Tp) defined.
   *
   *  @tparam  _Tp  The type to write to the ostream.
   *  @tparam  _CharT  The ostream char_type.
   *  @tparam  _Traits  The ostream char_traits.
  */
template<typename _Tp, typename _CharT = char,
    typename _Traits = char_traits<_CharT> >
class ostream_iterator
    : public iterator<output_iterator_tag, void, void, void, void> {
 public:
  ///@{
  /// Public typedef
#if __cplusplus > 201703L
  using difference_type = ptrdiff_t;
#endif
  typedef _CharT char_type;
  typedef _Traits traits_type;
  typedef basic_ostream<_CharT, _Traits> ostream_type;
  ///@}

 private:
  ostream_type *_M_stream;
  const _CharT *_M_string;

 public:
#if __cplusplus > 201703L

  constexpr ostream_iterator() noexcept
      : _M_stream(nullptr), _M_string(nullptr) {}

#endif

  /// Construct from an ostream.
  ostream_iterator(ostream_type &__s)
      : _M_stream(std::__addressof(__s)), _M_string(0) {}

  /**
   *  Construct from an ostream.
   *
   *  The delimiter string @a c is written to the stream after every Tp
   *  written to the stream.  The delimiter is not copied, and thus must
   *  not be destroyed while this iterator is in use.
   *
   *  @param  __s  Underlying ostream to write to.
   *  @param  __c  CharT delimiter string to insert.
  */
  ostream_iterator(ostream_type &__s, const _CharT *__c)
      : _M_stream(std::__addressof(__s)), _M_string(__c) {}

  /// Copy constructor.
  ostream_iterator(const ostream_iterator &__obj)
      : _M_stream(__obj._M_stream), _M_string(__obj._M_string) {}

#if __cplusplus >= 201103L

  ostream_iterator &operator=(const ostream_iterator &) = default;

#endif

  /// Writes @a value to underlying ostream using operator<<.  If
  /// constructed with delimiter string, writes delimiter to ostream.
  ostream_iterator &
  operator=(const _Tp &__value) {
    __glibcxx_requires_cond(_M_stream != 0,
                            _M_message(__gnu_debug::__msg_output_ostream)
                                ._M_iterator(*this));
    *_M_stream << __value;
    if (_M_string)
      *_M_stream << _M_string;
    return *this;
  }

  ostream_iterator &
  operator*() { return *this; }

  ostream_iterator &
  operator++() { return *this; }

  ostream_iterator &
  operator++(int) { return *this; }
};

# istream_iterator 的使用

/**
 * @addtogroup iterators
 * @{
 */

/// Provides input iterator semantics for streams.
template<typename _Tp, typename _CharT = char,
    typename _Traits = char_traits<_CharT>, typename _Dist = ptrdiff_t>
class istream_iterator
    : public iterator<input_iterator_tag, _Tp, _Dist, const _Tp *, const _Tp &> {
 public:
  typedef _CharT char_type;
  typedef _Traits traits_type;
  typedef basic_istream<_CharT, _Traits> istream_type;

 private:
  istream_type *_M_stream;
  _Tp _M_value;
  // This bool becomes false at end-of-stream. It should be sufficient to
  // check _M_stream != nullptr instead, but historically we did not set
  // _M_stream to null when reaching the end, so we need to keep this flag.
  bool _M_ok;

 public:
  ///  Construct end of input stream iterator.
  _GLIBCXX_CONSTEXPR istream_iterator()
      : _M_stream(0), _M_value(), _M_ok(false) {}

  ///  Construct start of input stream iterator.
  istream_iterator(istream_type &__s)
      : _M_stream(std::__addressof(__s)), _M_ok(true) { _M_read(); }

  istream_iterator(const istream_iterator &__obj)
      : _M_stream(__obj._M_stream), _M_value(__obj._M_value),
        _M_ok(__obj._M_ok) {}

#if __cplusplus > 201703L && __cpp_lib_concepts
  constexpr
      istream_iterator(default_sentinel_t)
      noexcept(is_nothrow_default_constructible_v<_Tp>)
      : istream_iterator() { }
#endif

#if __cplusplus >= 201103L

  istream_iterator &operator=(const istream_iterator &) = default;

  ~istream_iterator() = default;

#endif

  const _Tp &
  operator*() const {
    __glibcxx_requires_cond(_M_ok,
                            _M_message(__gnu_debug::__msg_deref_istream)
                                ._M_iterator(*this));
    return _M_value;
  }

  const _Tp *
  operator->() const { return std::__addressof((operator*())); }

  istream_iterator &
  operator++() {
    __glibcxx_requires_cond(_M_ok,
                            _M_message(__gnu_debug::__msg_inc_istream)
                                ._M_iterator(*this));
    _M_read();
    return *this;
  }

  istream_iterator
  operator++(int) {
    __glibcxx_requires_cond(_M_ok,
                            _M_message(__gnu_debug::__msg_inc_istream)
                                ._M_iterator(*this));
    istream_iterator __tmp = *this;
    _M_read();
    return __tmp;
  }

 private:
  bool
  _M_equal(const istream_iterator &__x) const {
    // Ideally this would just return _M_stream == __x._M_stream,
    // but code compiled with old versions never sets _M_stream to null.
    return (_M_ok == __x._M_ok) && (!_M_ok || _M_stream == __x._M_stream);
  }

  void
  _M_read() {
    if (_M_stream && !(*_M_stream >> _M_value)) {
      _M_stream = 0;
      _M_ok = false;
    }
  }

  /// Return true if the iterators refer to the same stream,
  /// or are both at end-of-stream.
  friend bool
  operator==(const istream_iterator &__x, const istream_iterator &__y) { return __x._M_equal(__y); }

  /// Return true if the iterators refer to different streams,
  /// or if one is at end-of-stream and the other is not.
  friend bool
  operator!=(const istream_iterator &__x, const istream_iterator &__y) { return !__x._M_equal(__y); }

#if __cplusplus > 201703L && __cpp_lib_concepts
  friend bool
      operator==(const istream_iterator& __i, default_sentinel_t)
      { return !__i._M_stream; }
#endif
};

back_insert_iterator 类的源码

// 24.4.2.2.1 back_insert_iterator
/**
 *  @brief  Turns assignment into insertion.
 *
 *  These are output iterators, constructed from a container-of-T.
 *  Assigning a T to the iterator appends it to the container using
 *  push_back.
 *
 *  Tip:  Using the back_inserter function to create these iterators can
 *  save typing.
*/
template<typename _Container>
class back_insert_iterator
    : public iterator<output_iterator_tag, void, void, void, void> {
 protected:
  _Container *container;

 public:
  /// A nested typedef for the type of whatever container you used.
  typedef _Container container_type;
#if __cplusplus > 201703L
  using difference_type = ptrdiff_t;

  constexpr back_insert_iterator() noexcept: container(nullptr) {}

#endif

  /// The only way to create this %iterator is with a container.
  explicit _GLIBCXX20_CONSTEXPR
  back_insert_iterator(_Container &__x)
      : container(std::__addressof(__x)) {}

  /**
   *  @param  __value  An instance of whatever type
   *                 container_type::const_reference is; presumably a
   *                 reference-to-const T for container<T>.
   *  @return  This %iterator, for chained operations.
   *
   *  This kind of %iterator doesn't really have a @a position in the
   *  container (you can think of the position as being permanently at
   *  the end, if you like).  Assigning a value to the %iterator will
   *  always append the value to the end of the container.
  */
#if __cplusplus < 201103L
  back_insert_iterator&
      operator=(typename _Container::const_reference __value)
      {
    container->push_back(__value);
    return *this;
      }
#else
  _GLIBCXX20_CONSTEXPR
      back_insert_iterator
  &

  operator=(const typename _Container::value_type &__value) {
    container->push_back(__value);
    return *this;
  }

  _GLIBCXX20_CONSTEXPR
      back_insert_iterator
  &

  operator=(typename _Container::value_type &&__value) {
    container->push_back(std::move(__value));
    return *this;
  }

#endif

  /// Simply returns *this.
  _GLIBCXX20_CONSTEXPR
      back_insert_iterator
  &

  operator*() { return *this; }

  /// Simply returns *this.  (This %iterator does not @a move.)
  _GLIBCXX20_CONSTEXPR
      back_insert_iterator
  &

  operator++() { return *this; }

  /// Simply returns *this.  (This %iterator does not @a move.)
  _GLIBCXX20_CONSTEXPR
      back_insert_iterator

  operator++(int) { return *this; }
};

# 三组插入迭代器

std:: back_inserter 函数模板的返回类型是类模板 back_insert_iterator ,该类模板的底层调用了容器的 push_back 函数。

std:: front_inserter 函数模板的返回类型是类模板 front_insert_iterator ,该类模板的底层调用了容器的 push_front 函数

std:: inserter 函数模板的返回类型是类模板 insert_iterator ,该类模板的底层调用了容器的 insert 函数。

# 反向迭代器

image-20241017094155099.png

vector<int> vec = {1, 3, 5, 7, 9, 10, 4};
vector<int>::reverse_iterator rit = vec.rbegin();
for(; rit != vec.rend(); ++rit) {
  cout << *rit << "  ";
}
cout << endl;