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。
接下来主要讨论的是反向迭代器的一个很特殊、也很容易出错的性质,即它的“逻辑位置”与“物理位置”。先通过看一个例子开始:
- vector<int> vec;
- for(vector<int>::size_type i=1; i<10; ++i)
- {
- vec.push_back(i);
- }
- vector<int>::iterator itr = vec.begin()+4;
- cout<<*itr<<endl;
- vector<int>::reverse_iterator r_itr(itr);
- 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;
}
}
C++迭代器之'反向迭代器'的更多相关文章
- C++迭代器之'插入迭代器
1. 定义 插入型迭代器(Insert Iterator),又叫插入器(Inserter). 2. 作用 插入迭代器的主要功能为把一个赋值操作转换为把相应的值插入容器的操作.算法库对所有在容器上的操作 ...
- 8、如何实现可迭代对象和迭代器对象 9、如何使用生成器函数实现可迭代对象 10、如何进行反向迭代以及如何实现反向迭代 11、如何对迭代器做切片操作 12、如何在一个for语句中迭代多个可迭代对象
8.如何实现可迭代对象和迭代器对象 PS:注意重载Iterator方法的时候,需要和原来的方法名一样,否则创建实例时会报错 from collections import Iterator,Itera ...
- Python之可迭代对象、迭代器、生成器
在使用Python的过程中,很容易混淆如下几个关联的概念: 1.容器(container) 2.可迭代对象(Iterable) 3.迭代器(Iterator) 4.生成器(generator) 5.生 ...
- 【C++】反向迭代器(rbegin,rend)(转载)
转自:http://blog.csdn.net/kjing/article/details/6936325 rbegin和rend,很有用! C++ primer (中文版第四版)第273页 9.3. ...
- Python中的可迭代对象与迭代器对象
刚刚学习Python,对“可迭代对象”和"迭代器对象"的个人理解,不知道对不对. 1.几个概念 (1)迭代工具:包括for循环.列表解析.in成员关系测试.....等等在内的,用于 ...
- Python可迭代对象、迭代器和生成器
Python可迭代对象.迭代器和生成器 python 函数 表达式 序列 count utf-8 云栖征文 python可迭代对象 python迭代器 python生成器 摘要: 8.1 可迭代对象( ...
- Python之列表生成式、生成器、可迭代对象与迭代器
本节内容 语法糖的概念 列表生成式 生成器(Generator) 可迭代对象(Iterable) 迭代器(Iterator) Iterable.Iterator与Generator之间的关系 一.语法 ...
- 完全理解Python迭代对象、迭代器、生成器
在了解Python的数据结构时,容器(container).可迭代对象(iterable).迭代器(iterator).生成器(generator).列表/集合/字典推导式(list,set,dict ...
- Python 迭代器之列表解析
 [TOC] 尽管while和for循环能够执行大多数重复性任务, 但是由于序列的迭代需求如此常见和广泛, 以至于Python提供了额外的工具以使其更简单和高效. 迭代器在Python中是以C语言的 ...
随机推荐
- 51Nod 1022 石子归并 V2(区间DP+四边形优化)
题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1022 题目大意: N堆石子摆成一个环.现要将石子有次序地合并成 ...
- 经验分享:如何系统学习 Web 前端技术?
这篇文章主要是面向小白用户的,如果你有些基础,当然也建议你看看,尤其是最后一个主题,或许你能得到一些启发.本文的观点,纯属个人自以为是的想法,不是真理,仅供参考. 抛开具体技术细节,先主要谈谈程序员如 ...
- jquery中的done和always解决ajax问题
昨天写一个跨域请求json数据的实例.遇到传值问题,尝试了各种方式都不行,后来发现,同一个地址,同一个ip请求次数频繁传值相同的话,ajax会默认跟一个&?时间戳,这就导致我传过去的值是错误的 ...
- TCP握手协议简述
TCP握手协议简述在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接.第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器 ...
- python模式匹配,提取指定字段
re匹配时分多行模式(re.M)与单行模式(rs.S),多行模式是每一行单独匹配,单行模式是把所有的行当成一行来匹配. 单行模式下.可以匹配换行符. ^$匹配所有字符 import re s='1_2 ...
- textbox获取当前光标位置,在光标后面插入字符串
左侧输入要复制的字符串,点击复制btn,将左侧字符串复制到后侧, 示例: 输入框 复制后 12345 12345 (光标落在3后) aaa 123aaa45(光标落在4后 ...
- HDU 4891 The Great Pan
模拟题. #include<map> #include<set> #include<ctime> #include<cmath> #include< ...
- PDF文档盖章
概述 在pdf文档的最后一页,合适位置,添加印章图片. maven依赖 <dependency> <groupId>com.itextpdf</groupId> & ...
- Linux命令之whereis
whereis [选项] [文件] (1).选项 -b 只搜索二进制文件 -B <目录> 只在路径下查找二进制文件 -m 只搜索man手册 -M <目录> 只在路径下查找man ...
- Linux的文件描述符
(1).文件描述符的定义 文件描述符是内核为了高效管理已被打开的文件所创建的索引,用于指向被打开的文件,所有执行I/O操作的系统调用都通过文件描述符:文件描述符是一个简单的非负整数,用以表明每个被进程 ...