• C++【STL】【反向迭代器】


    目录

    一、反向迭代器的简介

    1.什么是反向迭代器

    2.方向迭代器的实现

    二、反向迭代器的相关问题

    1.为什么在operator*中需要--迭代器

    2.适配list的反向迭代器

    3.适配vector的反向迭代器

    4.迭代器的分类

    5.迭代器萃取浅述


    一、反向迭代器的简介

    1.什么是反向迭代器

    12345

    如果是正向迭代器就是从1->2->3->4->5,也就是正向迭代器的++是向右边走

    但是我们的反向迭代器的++是向左边走,也就是5->4->3->2->1

    1. void test_list1()
    2. {
    3. list<int> lt;
    4. lt.push_back(1);
    5. lt.push_back(2);
    6. lt.push_back(3);
    7. lt.push_back(4);
    8. lt.push_back(5);
    9. //正向迭代器
    10. list<int>::iterator it = lt.begin();
    11. while (it != lt.end())
    12. {
    13. cout << *it << " ";
    14. ++it;
    15. }
    16. cout << endl;
    17. //反向迭代器
    18. list<int>::reverse_iterator rit = lt.rbegin();
    19. while (rit != lt.rend())
    20. {
    21. cout << *rit << " ";
    22. ++rit;
    23. }
    24. cout << endl;
    25. }

    2.方向迭代器的实现

    普通思维:拷贝一份正向迭代器,修改一下,搞出我们的方向迭代器

    大佬的思维:list是可以通过将遍历p.next变成p.prev,从而在++的时候实现反向迭代,

    1.但是vector怎么办呢?因为vector的迭代器底层是原生指针,++就只会往后走。

    vector的反向迭代器可以这样生成一个类,直接将++的方式重载成--,从而实现反向迭代

    1. struct_vector_iterator
    2. {
    3. T *_ptr;
    4. operator++()
    5. {
    6. --_ptr;
    7. }
    8. }

    2.采用复用(正向迭代器)的方式。

    从我们下面的STL源码中可以看到我们的STL其实是采用复用正向迭代器的方式的 

    一下是我们STL中关于反向迭代器的源码 

    1. template <class Iterator>
    2. class reverse_iterator
    3. {
    4. protected:
    5. Iterator current;
    6. public:
    7. typedef typename iterator_traits::iterator_category
    8. iterator_category;
    9. typedef typename iterator_traits::value_type
    10. value_type;
    11. typedef typename iterator_traits::difference_type
    12. difference_type;
    13. typedef typename iterator_traits::pointer
    14. pointer;
    15. typedef typename iterator_traits::reference
    16. reference;
    17. typedef Iterator iterator_type;
    18. typedef reverse_iterator self;
    19. public:
    20. reverse_iterator() {}
    21. explicit reverse_iterator(iterator_type x) : current(x) {}
    22. reverse_iterator(const self& x) : current(x.current) {}
    23. #ifdef __STL_MEMBER_TEMPLATES
    24. template <class Iter>
    25. reverse_iterator(const reverse_iterator& x) : current(x.current) {}
    26. #endif /* __STL_MEMBER_TEMPLATES */
    27. iterator_type base() const { return current; }
    28. reference operator*() const {
    29. Iterator tmp = current;
    30. return *--tmp;
    31. }
    32. #ifndef __SGI_STL_NO_ARROW_OPERATOR
    33. pointer operator->() const { return &(operator*()); }
    34. #endif /* __SGI_STL_NO_ARROW_OPERATOR */
    35. self& operator++() {
    36. --current;
    37. return *this;
    38. }
    39. self operator++(int) {
    40. self tmp = *this;
    41. --current;
    42. return tmp;
    43. }
    44. self& operator--() {
    45. ++current;
    46. return *this;
    47. }
    48. self operator--(int) {
    49. self tmp = *this;
    50. ++current;
    51. return tmp;
    52. }
    53. self operator+(difference_type n) const {
    54. return self(current - n);
    55. }
    56. self& operator+=(difference_type n) {
    57. current -= n;
    58. return *this;
    59. }
    60. self operator-(difference_type n) const {
    61. return self(current + n);
    62. }
    63. self& operator-=(difference_type n) {
    64. current += n;
    65. return *this;
    66. }
    67. reference operator[](difference_type n) const { return *(*this + n); }
    68. };

    二、反向迭代器的相关问题

    1.为什么在operator*中需要--迭代器

    这里我们观察到operator*,也就是解析迭代器所指向的数据,竟然需要先--迭代器,这是为什么呢?

    我们的STL源码中的迭代器的是这样定义的

    1. iterator begin() {return (link_type)(*node).next);}
    2. iterator end(){ return node;}

     

    这样就设计了一个对称的结构,正向迭代器的开始就是反向迭代器的结束,反向迭代器的开始就是正向迭代器的结束 

    1. reverse_iterator rbegin(){return reverse_iterator(end());}
    2. reverse_iterator rend() {return reverse_iterator(begin());}

    所以说我们的迭代器指针指向1前面的那个头结点的时候,将current的值赋给tmp,tmp--就到了5的位置然后将5的值返回,在5的位置的时候解引用得到的是4,在4位置解引用得到的是3,在3的位置解引用得到的是2,在2位置解引用,得到的是1,到了1,也就是我们rend()的位置也就结束了。

    当然我们将rebegin()放在5的位置也是可以的,那样的话就不用--再解引用了,这两种方法都是可以的。

    2.适配list的反向迭代器

    这是定义在reverse_iterator中的代码

    由于上面的1中已经介绍过为什么STL中国的源码采用先--在解引用的写法,所以我们下面的写法中,我们也是先--再引用。

    1. #pragma once
    2. namespace zhuyuan
    3. {
    4. // 复用,迭代器适配器
    5. //Iterator是用来定义反向迭代器的模板的,
    6. // Ref是用来里定义反向迭代器的数据类型,是const类型的就是const类型的迭代器,不是const类型的,就不是const类型的迭代器
    7. // Ptr定义的是结构体指针的模板
    8. template<class Iterator, class Ref, class Ptr>
    9. struct __reverse_iterator
    10. {
    11. Iterator _cur;
    12. typedef __reverse_iterator RIterator;
    13. __reverse_iterator(Iterator it)
    14. :_cur(it)
    15. {}
    16. //反向迭代器的++就是--,这里我们需要重载一下
    17. RIterator operator++()
    18. {
    19. --_cur;
    20. return *this;
    21. }
    22. //反向迭代器--就是++,这里我们需要重载一下
    23. RIterator operator--()
    24. {
    25. ++_cur;
    26. return *this;
    27. }
    28. Ref operator*()
    29. {
    30. //return *_cur;
    31. //解引用是不能够改变当前的数据的,所以我们用一个tmp对象来拷贝一下
    32. auto tmp = _cur;
    33. --tmp;
    34. return *tmp;
    35. }
    36. Ptr operator->()
    37. {
    38. //这是一个结构体指针,
    39. //这两种写法是一样的
    40. //return _cur.operator->();
    41. return &(operator*());
    42. }
    43. bool operator!=(const RIterator& it)
    44. {
    45. return _cur != it._cur;
    46. }
    47. };
    48. }

    然后在我们的list.h中的list类中封装反向迭代器对象,然后再list.h引入我们上面的reverse_iterator.h文件

    1. //封装正向迭代器
    2. typedef __list_iterator iterator;
    3. typedef __list_iteratorconst T&, const T*> const_iterator;
    4. //封装反向迭代器
    5. typedef __reverse_iterator reverse_iterator;
    6. typedef __reverse_iteratorconst T&, const T*> const_reverse_iterator;
    1. reverse_iterator rbegin()
    2. {
    3. return reverse_iterator(end());
    4. }
    5. reverse_iterator rend()
    6. {
    7. return reverse_iterator(begin());
    8. }

    测试代码

    1. void test_list1()
    2. {
    3. list<int> lt;
    4. lt.push_back(1);
    5. lt.push_back(2);
    6. lt.push_back(3);
    7. lt.push_back(4);
    8. lt.push_back(5);
    9. list<int>::iterator it = lt.begin();
    10. while (it != lt.end())
    11. {
    12. cout << *it << " ";
    13. ++it;
    14. }
    15. cout << endl;
    16. list<int>::reverse_iterator rit = lt.rbegin();
    17. while (rit != lt.rend())
    18. {
    19. cout << *rit << " ";
    20. ++rit;
    21. }
    22. cout << endl;
    23. it = lt.begin();
    24. while (it != lt.end())
    25. {
    26. *it *= 2;
    27. ++it;
    28. }
    29. cout << endl;
    30. for (auto e : lt)
    31. {
    32. cout << e << " ";
    33. }
    34. cout << endl;
    35. }

    3.适配vector的反向迭代器

    在我们的vector类中定义下面的方法

    然后再定义引用#include "reverse_iterator.h",也就是我们之前写的reverse_iterator.h文件

    1. typedef T* iterator;
    2. typedef const T* const_iterator;
    3. typedef __reverse_iterator reverse_iterator;
    4. typedef __reverse_iteratorconst T&, const T*> const_reverse_iterator;
    5. reverse_iterator rbegin()
    6. {
    7. return reverse_iterator(end());
    8. }
    9. reverse_iterator rend()
    10. {
    11. return reverse_iterator(begin());
    12. }

    测试代码

    1. void test_vector1()
    2. {
    3. vector<int> v;
    4. v.push_back(1);
    5. v.push_back(2);
    6. v.push_back(3);
    7. v.push_back(4);
    8. v.push_back(5);
    9. vector<int>::iterator it = v.begin();
    10. while (it != v.end())
    11. {
    12. cout << *it << " ";
    13. ++it;
    14. }
    15. cout << endl;
    16. vector<int>::reverse_iterator rit = v.rbegin();
    17. while (rit != v.rend())
    18. {
    19. cout << *rit << " ";
    20. ++rit;
    21. }
    22. cout << endl;
    23. }

     

    也就是说我们的rbegin()指向end()的位置也就是最后一个元素的下一个位置,解引用的时候,先--也就是到5的位置然后返回5,然后在5的位置,解引用的时候先--,到4的位置再返回4,以此类推,然后在2的位置解引用先--到1的位置,然后返回1,然后在1的位置到了rend()结束。 

    4.迭代器的分类

    这里我们的vector和list都是可以用我们上面写的reverse_iterator.h文件的,也就是说我们其实是可以实现大量的复用的!!

    我们的反向迭代器中只要是正向迭代器中可以实现的,我们反向迭代器都可以实现。

    反向迭代器:迭代器适配器,可以适配生成支持++和--的容器的反向迭代器。

    一般的容器都是支持++的,但是--并不是所有的容器都支持的

    (forward_list(单链表,forward是单向的意思,bidrectional是双向的意思),unodered_map(哈希表)和unordered_set)

    迭代器从功能的角度分类:

    forward_list:只支持++(forward_list,unordered_map,unordered_set)

    bidrectional:支持++也支持--(list,map,set)

    random_access_iterator:除了支持++也支持--还支持+和-(比方说从第三个位置+5直接到了第八个位置)(vector,deque)

    我们注意到 cplusplus的官方网站中写模板函数的时候也是按照迭代器的功能分类的

    (这里的名称暗示你sort的容器得是随机迭代器,reverse的名称暗示你,sort的容器得是双向迭代器(当然随机迭代器也支持双向迭代器,其实是继承关系))

     

    5.迭代器萃取浅述

    这是stl_list中的部分源码

    1. #ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
    2. typedef reverse_iterator const_reverse_iterator;
    3. typedef reverse_iterator reverse_iterator;
    4. #else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
    5. typedef reverse_bidirectional_iterator
    6. const_reference, difference_type>
    7. const_reverse_iterator;
    8. typedef reverse_bidirectional_iterator
    9. difference_type>
    10. reverse_iterator;
    11. #endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */

    这是老版本中的定义,它将value_type和reference全部都传递过去

     也就是我们上面的自己写的

    而在新的版本中,我们注意到它只将这里的迭代器传了过去

    那按照我们的理解,那要是我们将自己写的代码中的ref和ptr去掉,那我们下面的两个函数就不知道应该返回什么类型了。

     这里就是用到了迭代器萃取

    萃取可以用来提高运算的效率。

    适配器真正的特点就是实现了复用。也就是上层的封装,转换出我们想要的东西。

  • 相关阅读:
    思考(八十九):WAL 实现
    MAC电脑连接外接显示屏,颜色显示有问题,又粉、紫色蒙版,问题处理(1)
    华南X99平台打鸡血教程
    LeetCode·139.单词拆分·递归·记忆化搜索·字典树
    vellum 学习03 10/7 (知识补)
    linux 内核链表详解
    初始 c++(1)
    springboot 整合使用redis发布订阅功能
    String vs StringBuffer vs StringBuilder
    三、我的/登录 栏制作《仿淘票票系统前后端完全制作(除支付外)》
  • 原文地址:https://blog.csdn.net/weixin_62684026/article/details/127095483