• 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;
    }
    View Code

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

    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
    View Code

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

    双向迭代器以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;
    }
  • 相关阅读:
    冲刺第十三天
    冲刺第十二天
    冲刺第十一天
    Android Studio三种运行方法
    第十三周学习进度
    冲刺第一阶段意见评论
    第十二周学习进度
    冲刺第十天
    大二暑假周总结(五)
    大二暑假周总结(四)
  • 原文地址:https://www.cnblogs.com/LearningTheLoad/p/7466560.html
Copyright © 2020-2023  润新知