STL标准库-迭代器

摘要: 摘要: 技术在于交流、沟通,本文为博主原创文章转载请注明出处并保持作品的完整性

本节主要介绍STL六大部件中的Iterators迭代器.

在语言方面讲,容器是一个class template, 算法是一个仿函数, 分配器class template, 迭代器是一个class template, 适配器class template, 分配器class template

从图中我们可以看出算法是看不到容器的,容器跟算法的交互必要用迭代器做桥梁,那么迭代器是怎样让容器和算法满足各自的需求的呢?

我们先看一下都有哪些迭代器

struct input_iterator_tag{};//write-read迭代器
struct output_iterator_tag{};//read-only输出流
struct forward_iterator_tag : public input_iterator_tag{}//单向迭代器 forward_list multiset/map;
struct bidirectional_iterator_tag : public input_iterator_tag{}//双向迭代器 hash list set map;
struct random_access_iterator_tag : public input_iterator_tag{}//随机迭代器 array vector deque;

类图如下

下面我们验证一下各种容器的迭代器

这是测试代码

#include <iostream>
#include <iterator>
#include <array>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <forward_list>
#include <deque>
#include <unordered_set>
#include <unordered_map>

using namespace std;

void _display_category(input_iterator_tag)
{
    cout << "input_iterator_tag" << endl;
}

void _display_category(output_iterator_tag)
{
    cout << "output_iterator_tag" << endl;
}

void _display_category(forward_iterator_tag)
{
    cout << "forward_iterator_tag" << endl;
}

void _display_category(bidirectional_iterator_tag)
{
    cout << "bidirectional_iterator_tag" << endl;
}

void _display_category(random_access_iterator_tag)
{
    cout << "random_access_iterator_tag" << endl;
}

template<typename I>
void display_ccategory(I iter)
{
    typename iterator_traits<I>::iterator_category cagy;
    _display_category(cagy);
}

int main()
{
    display_ccategory(array<int, 10>::iterator());
    display_ccategory(vector<int>::iterator());
    display_ccategory(list<int>::iterator());
    display_ccategory(forward_list<int>::iterator());
    display_ccategory(deque<int>::iterator());
    cout << endl;
    display_ccategory(set<int>::iterator());
    display_ccategory(multiset<int>::iterator());
    display_ccategory(map<int,int>::iterator());
    display_ccategory(multimap<int,int>::iterator());
    cout << endl;
    display_ccategory(unordered_set<int>::iterator());
    display_ccategory(unordered_multiset<int>::iterator());
    display_ccategory(unordered_map<int,int>::iterator());
    display_ccategory(unordered_multimap<int,int>::iterator());

    return 0;
}



迭代器是怎样让容器和算法满足各自的需求的呢

template<typename _Iterator, typename _Container>
    class __normal_iterator
    {
    protected:
      _Iterator _M_current;

      typedef iterator_traits<_Iterator>        __traits_type;

    public:
      typedef _Iterator                    iterator_type;
      typedef typename __traits_type::iterator_category iterator_category;
      typedef typename __traits_type::value_type      value_type;
      typedef typename __traits_type::difference_type     difference_type;
      typedef typename __traits_type::reference     reference;
      typedef typename __traits_type::pointer       pointer;
    ...
}

iterator_category,表示迭代器的分类(上面的5中类型)

value_type,表示你的value类型(vecotr<int>,此时的value_type就是int)

difference_type,表示两个迭代器指针间的距离(如begin()和end()间的距离)

pointer,表示指针(没看到使用)

reference,表示引用(没看到使用)

其实算法-容器-迭代器他们之间的交互就是用这五种变量,容器创建迭代器时,迭代器获取这五个变量,容器调用算法时,算法获取迭代器的这五个变量.

下面以distance()为例,distance()的方法是算出距离,我们看其源码

namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION

  // There are two signatures for distance.  In addition to the one
  // taking two iterators and returning a result, there is another
  // taking two iterators and a reference-to-result variable, and
  // returning nothing.  The latter seems to be an SGI extension.
  // -- pedwards
  template<typename _InputIterator, typename _Distance>
    inline void
    __distance(_InputIterator __first, _InputIterator __last,
           _Distance& __n, std::input_iterator_tag)//共有三个变量,分别是__first(起始位置),__lase(终点位置),迭代器分类
    {                            //此时的迭代器分列是input_iterator_tag,也就意味着这是一种泛华类型,input_iterator_tag他的子类都可以调用这个方法
      // concept requirements               //该方法主要负责内存不连续的容器,如bidirectional_iterator_tag
      __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
      while (__first != __last)
    {
      ++__first;
      ++__n;
    }
    }

  template<typename _RandomAccessIterator, typename _Distance>
    inline void
    __distance(_RandomAccessIterator __first, _RandomAccessIterator __last,
           _Distance& __n, std::random_access_iterator_tag)//该方法主要负责内存连续的容器使用
    {
      // concept requirements
      __glibcxx_function_requires(_RandomAccessIteratorConcept<
                  _RandomAccessIterator>)
      __n += __last - __first;
    }

  /**
   *  This is an SGI extension.
   *  @ingroup SGIextensions
   *  @doctodo
  */
  template<typename _InputIterator, typename _Distance>
    inline void
    distance(_InputIterator __first, _InputIterator __last,
             _Distance& __n)
    {
      // concept requirements -- taken care of in __distance
      __distance(__first, __last, __n, std::__iterator_category(__first));//根据函数重载调用各自函数
    }

_GLIBCXX_END_NAMESPACE_VERSION
} // namespace

现在我们来分析一下内存连续的迭代器(random_access_iterator_tag),与内存不连续的迭代器()分别调用__distance(bidirectional_iterator_tag)这个方法时的效率

假设__first与__last的距离为1000000

那么当bidirectional_iterator_tag调用distance()时

      __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
      while (__first != __last)
    {
      ++__first;
      ++__n;
    }

也就意味着这个函数需要++一百万次

而内存连续的random_access_iterator_tag调用distance时呢

  template<typename _RandomAccessIterator, typename _Distance>
    inline void
    __distance(_RandomAccessIterator __first, _RandomAccessIterator __last,
           _Distance& __n, std::random_access_iterator_tag)
    {
      // concept requirements
      __glibcxx_function_requires(_RandomAccessIteratorConcept<
                  _RandomAccessIterator>)
      __n += __last - __first;
    }

只需要走一次,此时你应该体验到迭代器对效率的影响了吧

还有advance()函数,我把其源码粘在下面

template<typename _InputIterator, typename _Distance>
    inline void
    __advance(_InputIterator& __i, _Distance __n, input_iterator_tag)
    {
      // concept requirements
      __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
      _GLIBCXX_DEBUG_ASSERT(__n >= 0);
      while (__n--)
    ++__i;
    }

  template<typename _BidirectionalIterator, typename _Distance>
    inline void
    __advance(_BidirectionalIterator& __i, _Distance __n,
          bidirectional_iterator_tag)
    {
      // concept requirements
      __glibcxx_function_requires(_BidirectionalIteratorConcept<
                  _BidirectionalIterator>)
      if (__n > 0)
        while (__n--)
      ++__i;
      else
        while (__n++)
      --__i;
    }

  template<typename _RandomAccessIterator, typename _Distance>
    inline void
    __advance(_RandomAccessIterator& __i, _Distance __n,
              random_access_iterator_tag)
    {
      // concept requirements
      __glibcxx_function_requires(_RandomAccessIteratorConcept<
                  _RandomAccessIterator>)
      __i += __n;
    }

  /**
   *  @brief A generalization of pointer arithmetic.
   *  @param  __i  An input iterator.
   *  @param  __n  The @a delta by which to change @p __i.
   *  @return  Nothing.
   *
   *  This increments @p i by @p n.  For bidirectional and random access
   *  iterators, @p __n may be negative, in which case @p __i is decremented.
   *
   *  For random access iterators, this uses their @c + and @c - operations
   *  and are constant time.  For other %iterator classes they are linear time.
  */
  template<typename _InputIterator, typename _Distance>
    inline void
    advance(_InputIterator& __i, _Distance __n)
    {
      // concept requirements -- taken care of in __advance
      typename iterator_traits<_InputIterator>::difference_type __d = __n;
      std::__advance(__i, __d, std::__iterator_category(__i));
    }

#if __cplusplus >= 201103L

  template<typename _ForwardIterator>
    inline _ForwardIterator
    next(_ForwardIterator __x, typename
     iterator_traits<_ForwardIterator>::difference_type __n = 1)
    {
      std::advance(__x, __n);
      return __x;
    }

  template<typename _BidirectionalIterator>
    inline _BidirectionalIterator
    prev(_BidirectionalIterator __x, typename
     iterator_traits<_BidirectionalIterator>::difference_type __n = 1)
    {
      std::advance(__x, -__n);
      return __x;
    }

#endif // C++11

_GLIBCXX_END_NAMESPACE_VERSION
} // namespace



下面介绍一下迭代器的基本使用

双向迭代器以list为例

int main () {

    list<int> c = {1,2,3,4,5};
    list<int>::iterator iter = c.begin();
    list<int>::iterator iter1 = c.begin();

    //存取实际元素
    cout<< *iter <<endl;

    //向前步进(返回新位置)
    cout << *(++iter)  << endl;

    //向前步进(返回旧位置)
    cout << *(iter++)  << endl;

    //向后步进(返回新位置)
    cout << *(--iter)  << endl;

    //向后步进(返回旧位置)
    cout << *(iter--)  << endl;

    //迭代器赋值
    iter = ++iter;
    cout<< *iter <<endl;

    //判断两个迭代器是否相等  !=判断是否不相等
    cout<< (iter1 == iter) << endl;

    return 0;
}

随机迭代器以vector为例

int main () {

    vector<int> c = {1,2,3,4,5};
    vector<int>::iterator iter = c.begin();
    vector<int>::iterator iter1 = c.begin();

    //取下表为n的元素
    cout<<iter[3]<<endl;

    //向前跳n个元素(若n为负,则向后跳)
    cout<<*(iter+=1)<<endl;

    //传回iter1和iter2之间的距离
    cout<< iter1-iter<<endl;

    //判断iter1是否在iter之前
    cout<<(iter1<iter)<<endl;

    //判断iter1是否不在iter之后
    cout<<(iter1<=iter)<<endl;

    return 0;
}

迭代器的辅助函数

int main () {

    list<int> c = {1,2,3,4,5};
    list<int>::iterator iter = c.begin();
    list<int>::iterator iter1 = c.begin();

    //使迭代器前进给定的距离
    advance(iter, 3);
    cout << *iter <<endl;

    //返回两个迭代器之间的距离
    cout << distance(iter, iter1) <<endl;

    //使迭代器前进一步
    iter = next(iter);

    cout << *iter << endl;

    //使迭代器后退一步
    iter = prev(iter);
    cout << *iter << endl;

    return 0;
}
时间: 2024-08-25 13:36:59

STL标准库-迭代器的相关文章

STL标准库-容器-set与multiset

摘要: 技术在于交流.沟通,转载请注明出处并保持作品的完整性. set与multiset关联容器 结构如下 set是一种关联容器,key即value,value即key.它是自动排序,排序特点依据key set的key不能相同.multiset的key相同.关联容器的查找效率要高于顺序容器很多很多. set和multiset不提供用来直接存取元素的任何操作函数,取值需要通过迭代器 一 定义 1.set/mulitiset以红黑树为底层结构,因此有元素自动排序的特性,排序是根据key,而set.m

STL标准库-算法-常用算法

摘要: 摘要: 摘要: 技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 介绍11种STL标准库的算法,从这11种算法中总结一下算法的基本使用 1.accumulate() 累加 2.for_each() for一段区间 做你指定的行为 3.replace(), replace_if(), replace_copy() 替换函数 4.count(), count_if() 计数 5.find() 查找 6.sort() 排序 7.binary_search()查看元素是否在

参考C++STL标准库中对了的使用方法

http://www.cppblog.com/zhenglinbo/archive/2012/09/18/191170.html 参考:http://www.cppblog.com/zhenglinbo/archive/2012/09/18/191170.html 当然是使用c++中的STL 的queue啦.下面简要介绍一下使用方法. 1 准备工作 头文件 #include<queue> 2 声明和定义的方法.STL的队列是泛型模板,支持任何内置和构造类型. 比如对于刚才那个牛奶问题.我把状态

c/c++ 标准库 迭代器(iterator)

c/c++ 标准库 迭代器 begin和end运算符返回的具体类型由对象是否是常量决定,如果对象是常量,begin和end返回const_iterator:如果对象不是常量,返回iteraotor 1,但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素. 2,不能在范围for循环中向vector对象添加元素 标准库 迭代器(iterator)的小例子 test1~test8 #include <iostream> #include <string> #include &l

STL标准库-容器-list

摘要: 技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. list 表示非连续的内存区域,并通过一对指向首尾元素的指针双向链接起来,从而允许向前和向后两个方向进行遍历.在list 的任意位置插入和删除元素的效率都很高. 它的结构 一 定义 头文件 #include <vector> #include <iostream> #include <list> using namespace std; int main(int argc, const c

C++学习笔记之STL标准库(二)algorithm头文件即算法

#include <algorithm> algorithm头文件中主要包含的是一大堆模板函数,即STL库提供的算法,可以认为每个函数在很大程度上是独立的.提供的算法种类有: 1)adjacent_find //检测区间内第一对相等的相邻元素 template<class FwIt> FwIt adjacent_find(FwdIt first,FwdIt last);   //如果成功,返回first+N,N满足*(first+N) == *(first+N+1):如果不存在相等

STL标准库-容器-rb_tree

摘要: 摘要: 技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 红黑树,关联式容器底层实现(map set),在使用中基本运用不到,但是还是想了解一下他的运作方式 Red_Black tree是平衡二分搜寻树(balanced binary search tree),它是高度平衡的二叉树,这样有利于search和insert. 红黑树提供遍历,如果如果按正常规则(++iter)遍历,便能获得排序状态 如上图,你会发现返回迭代器头的begin()函数指向的是"5"

STL标准库-容器适配器

摘要: 技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 上一节介绍了仿函数适配器,这节主要介绍容器适配器和迭代器适配器的概念,其实容器适配器和迭代器其适配器就是封装了一些其他class的方法,非常好理解. 如果你想让一个calss拥有另一个class的功能,你都可以这样做:1.继承 2.包含 迭代器适配器 运用继承方式,实现适配功能,其实现与仿函数适配器相似. 容器适配器中主要运用的就是包含,即一个类含的一个成员变量是另一个类,本节简单介绍两个容器适配器 容器适配器 qu

STL标准库-容器-deque

摘要: 技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. deque双向开口可进可出的容器 我们知道连续内存的容器不能随意扩充,因为这样容易扩充别人那去 deque却可以,它创造了内存连续的假象. 其实deque由一段一段构成 ,他是分段连续,而不是内存连续 当走向段的尾端时候自动跳到下一段 所以支持迭代器++ 操作,自动跳到下一段的方法由operator++实现 deque每次扩充 申请一个段 一 定义 头文件 #include <deque> int main_0(