zoukankan      html  css  js  c++  java
  • C++迭代器之'反向迭代器'

    反向迭代器(Reverse Iterator)是普通迭代器的适配器,通过重新定义自增和自减操作,以达到按反序遍历元素的目的。如果在标准算法库中用反向迭代器来代替普通的迭代器,那么运行结果与正常情况下相反。除此之外,其用法与普通迭代器完全一样,我们不作详细讨论。

    反向迭代器reverse_iterator是一种反向遍历容器的迭代器,也就是从最后一个元素到第一个元素遍历容器。反向迭代器的自增(或自减)的含义反过来了:对于反向迭代器,++运算符将访问前一个元素,–运算符将访问下一个元素。 

    反向迭代器与迭代器的转换 

        reverse_iterator与iterator都继承自_Iterator_base,它们是可以相互转换的。 

    • 调用reverse_iterator的base()方法可以获取"对应的"iterator。 
    • 可以用iterator构造一个"对应的"reverse_iterator。 

       下面的两个例子展示了它们之间的转换: 

    list<int> test_list; 
    for (size_t i = 1; i < 8; i++) 

        test_list.push_back( i*2 ); 

    list<int>::reverse_iterator rit = find(test_list.rbegin(), test_list.rend(), 8); 
    list<int>::iterator it(rit.base()); 
    cout << *rit << endl; 
    cout << *it << endl;

       上面的代码是先查找到一个指向8的reverse_iterator,并reverse_iterator的base()方法得到一个iterator。但是从输出上看,iterator指向的元素的值并不是8,而是10。 

    list<int> test_list; 
    for (size_t i = 1; i < 8; i++) 

        test_list.push_back( i*2 ); 

    list<int>::iterator it = find(test_list.begin(), test_list.end(), 8); 
    list<int>::reverse_iterator rit(it); 
    cout << *it << endl; 
    cout << *rit << endl; 

       上面的代码是先查找一个指向8的iterator,然后用该iterator构造一个reverse_iterator。但是从输出上看,reverse_iterator指向的元素并不是8,而是6。 

           接下来主要讨论的是反向迭代器的一个很特殊、也很容易出错的性质,即它的“逻辑位置”与“物理位置”。先通过看一个例子开始:

    [cpp] view plain copy
     
    1. vector<int> vec;  
    2. for(vector<int>::size_type i=1; i<10; ++i)  
    3. {  
    4.     vec.push_back(i);  
    5. }  
    6.   
    7. vector<int>::iterator itr = vec.begin()+4;  
    8. cout<<*itr<<endl;  
    9. vector<int>::reverse_iterator r_itr(itr);  
    10. cout<<*r_itr<<endl;  

    这个例子中,vec中存放从1到9的9个连续数字,并初始化一个普通迭代器指向数字5,打印输出结果显而易见,为5。然后再初始化一个反向迭代器,该迭代器指向与普通迭代器一样的物理位置,然后打印输出,这时结果为多少呢?

           如果你不了解反向迭代器这个特殊的性质的话,很容易误认为结果一样是5。但实际情况不是这样,而是4,即前一个位置处的元素!具体原因,即涉及到反向迭代器的“物理位置”与“逻辑位置”两个概念。

           我们都知道,一个容器的范围用普通迭代器表示为一个“半开半闭”的区间。头部为begin,指向容器第一个元素的位置。末尾为end,指向最后一个元素的下一个位置,每个容器都提供了这样一个位置,尽管该位置不可引用,但却是个合法的地址。相反,第一个元素位置的前一个位置容器却没有任何保证,比如对于vector和string来说,就是非法的位置。这里我们说“合法”与“非法”,简单来讲,可以这样认为,一个合法的位置对于迭代器来说是可以达到的,像最后一个元素的下一个位置end()。而对于首元素的前一个位置,迭代器是无法指向它的,begin()-1这个表达式会导致异常。因此,反向迭代器与普通迭代器在物理位置上保持了一一对应,即rbegin()对应普通迭代器的end()位置,rend()对应其begin()位置。

           但是,为了让反向迭代器与普通迭代器在概念上保持一致性,即begin()(反向迭代器对应为rbegin())对应第一个元素(对于反向迭代器来说,最后一个元素即第一个元素),end()(反向迭代器对应为rend())对应最后一个元素的下一个位置,于是标准库的设计者们想出这样一个方法,即反向迭代器的逻辑位置等于其物理位置的前一个位置。换句话说,物理位置对应迭代器在内存中的实际位置,逻辑位置对应迭代器对应容器中元素的位置。这样,对于rbegin()来说,它物理位置是容器最后一个元素的下一个位置,逻辑位置即容器最后一个元素的位置(对反向迭代器来说就是第一个元素元素的位置),同理rend()物理位置为容器第一个元素位置,逻辑位置即第一个位置的前一个位置(依然不可解引用)。这样,反向迭代器与普通迭代器便有了一致的概念,即“半开半闭”区间。更为直观的演示如下图:

     

           (该图片来自:《The C++ Standard Library, A Tutorial And Reference》)

            这样,开头那个例子就很容易解释了,反向迭代器被初始化为与前一个普通迭代器一样的物理位置(对应元素5),其逻辑位置即前一个位置,因些通过解引用得到元素4.

    反向迭代器的操作 

       了解了反向迭代器与迭代器的关系,再来对容器进行操作就很简单了。下面是常用操作的例子,例子中用的test_list的内容如上图所示: 

    1. 遍历容器 

    for(list<int>::reverse_iterator rit = test_list.rbegin(); rit != test_list.rend(); ++rit) 

        cout<< *rit << " "; 

    2. 插入元素 

        假设要在rit指向的位置插入一个新元素150,那么150应该在8和10之间。对于rit而言,150应该插入到rit的前一个位置。对于it (it = rit.base()),150应该插入it的前一个位置。 

    所以,要实现在一个reverse_iterator rit指出的位置上插入新元素,在rit.base()指向的位置插入就行了。 

        操作代码如下: 

    list<int>::reverse_iterator rit = find(test_list.rbegin(), test_list.rend(), 8); 
    test_list.insert(rit.base(), 150); 

    3. 删除元素 

        假设要删除rit指向的元素,就不直能删除rit.base()指向的元素了,rit指向的是8,而rit.base()指向的是10。实际上我们要删除的是rit.base()的前一个元素。 

    所以,要实现在一个reverse_iterator rit指出的位置上删除元素,那么删除rit.base()的前一个元素就行了。 

    操作代码如下: 

    for(list<int>::reverse_iterator rit = test_list.rbegin(); rit != test_list.rend();) 

        if (8 == *rit) 
        { 
            list<int>::iterator it = –rit.base() ;  // 用(++rit).base()也可; 
            list<int>::iterator it_after_del = test_list.erase(it); 
            rit = list<int>::reverse_iterator(it_after_del); 
        } 
        else 
        { 
            ++rit; 
        } 

        调用erase()删除元素后,返回值是指向被删除的元素的下一个元素的iterator,所以还需要把返回的iterator变成reverse_iterator赋值给rit。 

    当然,上面的代码写成这样也是一个意思: 

    for(list<int>::reverse_iterator rit = test_list.rbegin(); rit != test_list.rend();) 

        if (8 == *rit) 
        { 
            rit = list<int>::reverse_iterator(test_list.erase(–rit.base())); 
        } 
        else 
        { 
            ++rit; 
        } 
    }

  • 相关阅读:
    Linux内核分析作业六
    课本第三章读书笔记
    课本第十八章读书笔记
    Linux内核分析作业五
    课本第五章读书笔记
    MSF MS11-050/10-087/Adobe攻击实践及内存保护技术
    Linux课题实践五——字符集总结与分析
    Linux课题实践四——ELF文件格式分析
    Linux课题实践三——程序破解
    实践二——内核模块
  • 原文地址:https://www.cnblogs.com/fnlingnzb-learner/p/6842298.html
Copyright © 2011-2022 走看看