STL中使用reverse_iterator时,如何正确使用erase函数
假设有一个list容器,顺序存储了0-9一个10个整数。现在要使用reverse_iterator迭代器来查找值为8和5的元素,并且将这两个数删除。先来看以下的解决方法:
#include <iostream>
#include <list>
using namespace std;
int main()
{
//MergeSortTest(); // 测试题目一,归并排序
//movableWindowsTest();// 测试题目二,移动窗口,输出窗口中的最小值
list<int> l;
for(int i = ;i < ;++i)
l.push_back(i);// 初始化容器存入0-9十个整数
list<int>::reverse_iterator it;
int count = ;// 记录剩下需要删除元素的个数
for(it = l.rbegin();it != l.rend() && count; ++it)
{
if(*it == || *it == )
{
list<int>::iterator temp(it.base());// 使用base函数返回一个普通迭代器,并以此删除指向的元素
++it;
l.erase(temp);
-- count;
}
}
return ;
}
我们重点看17-19行三行代码是否正确。这是我在不经过思考就顺手写出来的代码,这三行代码虽然编译可以通过,但是是错误的。
首先,我们知道不可以直接使用reverse_iterator作为参数来调用erase函数的。
(错误思路)我的初始思路是希望通过it.base()函数来返回一个普通迭代器,并使用普通迭代器来删除此元素,在删除之前,先将迭代器指向下一个元素。
这样做有两个地方引起错误:
1、当it指向元素8时,it.base()返回的迭代器指向it的前一个位置,即指向元素9;
2、使用erase删除it.base()后,it的值未定义,即删除后,it的值不存在了,而不是原来++it的值。
针对错误1,我们可以改使用(++it).base()来返回指向正确位置的迭代器。(请注意不要使用++it.base(),这样做修改返回值,是极不推荐的做法)。
针对错误2,我们需要知道,erase函数的返回值就是 删除该元素后指向的下一个位置的迭代器。因此只要记录下erase的返回值,就能正确获得it的值。
根据以上两点说法,将代码13-21行修改为:
for(it = l.rbegin();it != l.rend() && count;)
{
if(*it == || *it == )
{
list<int>::iterator iter = l.erase((++it).base());// 记录删除该元素后的指向下一个位置的迭代器(注意删除8时,下一个是9)
it = list<int>::reverse_iterator(iter);// 将该迭代器转换为reverse_iterator,此时it指向7
-- count;
}
else
++it;
}
以上代码变可以成功删除容器中的5和8两个元素,并使容器中存储的为1,2,3,4,6,7,9.
到此,仍有一个需要特别留意的问题:上面的第5行代码,删除8时,得出的iter指向的“下一个元素”是9,而不是7。而第六行执行完以后,it指向的是7。
其实这个应该挺好理解,但是在下水平有限,不太会解释。指出这一点是为了让大家多留个神,否则容易给自己挖了个坑。详细解释请查阅其他资料。以下为正确的完整代码写法:
#include <iostream>
#include <list>
using namespace std;
int main()
{
//MergeSortTest(); // 测试题目一,归并排序
//movableWindowsTest();// 测试题目二,移动窗口,输出窗口中的最小值
list<int> l;
for(int i = ;i < ;++i)
l.push_back(i);// 初始化容器存入0-9十个整数
list<int>::reverse_iterator it;
int count = ;// 记录剩下需要删除元素的个数
for(it = l.rbegin();it != l.rend() && count;)
{
if(*it == || *it == )
{
list<int>::iterator iter = l.erase((++it).base());// 记录删除该元素后的指向下一个位置的迭代器(注意删除8时,下一个是9)
it = list<int>::reverse_iterator(iter);// 将该迭代器转换为reverse_iterator,此时it指向7
-- count;
}
else
++it;
}
for(list<int>::iterator iter = l.begin();iter != l.end();++iter)
cout << *iter << " ";
cout << endl;
return ;
}
STL中使用reverse_iterator时,如何正确使用erase函数的更多相关文章
- C/C++-STL中lower_bound与upper_bound的用法以及cmp函数
转载于:http://blog.csdn.net/tjpuacm/article/details/26389441 不加比较函数的情况: int a[]={0,1,2,2,3}; printf(&qu ...
- 使用 QSqlTableModel 模型向数据库中插入数据时,为什么使用 rowCount 函数只能返回 256 最大值?
默认返回缓冲区里面的数据,如果你向要获取更多值,请在前面加入以下语句即可. while(model.canFetchMore()){ model.fetchMore(); } 该语句会获取更多的记录.
- [转] C++ STL中map.erase(it++)用法原理解析
总结一下map::erase的正确用法. 首先看一下在循环中使用vector::erase时我习惯的用法: for(vector<int>::iterator it = vecInt.be ...
- STL中的stack(堆栈)
转载:http://blog.csdn.net/morewindows/article/details/6950881 栈(statck)这种数据结构在计算机中是相当出名的.栈中的数据是先进后出的(F ...
- STL中的内存与效率
STL中的内存与效率 1. 使用reserve()函数提前设定容量大小,避免多次容量扩充操作导致效率低下. 关于STL容器,最令人称赞的特性之一就是是只要不超过它们的最大大小,它们就可以自动增长到足 ...
- STL中六大组件
1)容器(Container),是一种数据结构,如list,vector,和deques ,以模板类的方法提供.为了访问容器中的数据,可以使用由容器类输出的迭代器: 容器(container)用于存放 ...
- 深入了解STL中set与hash_set,hash表基础
一,set和hash_set简介 在STL中,set是以红黑树(RB-Tree)作为底层数据结构的,hash_set是以哈希表(Hash table)作为底层数据结构的.set可以在时间复杂度为O(l ...
- STL中erase的小心使用
先看如下一道改错题: #include<iostream> #include<vector> using namespace std; void print(vector< ...
- C++ STL中的常用容器浅谈
STL是C/C++开发中一个非常重要的模板,而其中定义的各种容器也是非常方便我们大家使用.下面,我们就浅谈某些常用的容器.这里我们不涉及容器的基本操作之类,只是要讨论一下各个容器其各自的特点.STL中 ...
随机推荐
- python学习之logging模块
Logger.setLevel(level) 设置记录器的级别为level.低于该级别的信息将被忽略. 记录器默认级别为NOTSET.如果记录器是根记录器,则默认将记录所有信息: 如果是一个非根记录器 ...
- java中的锁池和等待池
在java中,每个对象都有两个池,锁(monitor)池和等待池 wait() ,notifyAll(),notify() 三个方法都是Object类中的方法. 锁池:假设线程A已经拥有了某个对象(注 ...
- CCNA2.0笔记_IPv6
IPv6地址表示方法: 连续的零字段可表示为:: (每个地址只能用一次) 示例: 2031:0000:130F:0000:0000:09C0:876A:130B –可表示为2031:0:130f::9 ...
- 字符串操作:int 转 string
strstream ss; string ch; ss<<i; ss>>ch;
- oracle字符存储
substr和substrb 以前知道有substrb,lengthb等函数,也知道它们是以byte来计算长度,可没用过,也不太明白什么地方需要用到它们.一直就是用substr,length,以 ...
- Practial Vim 学习笔记一
. 号作用是重复上一个动作. >+G 缩进 j 光标下移 u 撤销操作 $ 光标移到行尾 x 删除光标下的字符 dd 删除整行 i 切换到Insert模式 Esc 返回 f 将光标移到下个字符 ...
- 说明Oracle数据库逻辑备份和物理备份的方式。
说明Oracle数据库逻辑备份和物理备份的方式. 解答:Oracle备份包括逻辑备份和物理备份. 1).逻辑备份 数据库的逻辑备份包含读一个数据库记录集和将记录集写入文件. a.输出(Export)输 ...
- 【vijos】1447 开关灯泡(高精度+特殊的技巧)
https://vijos.org/p/1447 一开始想了想似乎只想到了与约数个数有关,即约数个数为奇数那么显然是亮的. 竟然没想到完全平方数..sad.. 在正因子中,只有完全平方数的正因子才是奇 ...
- 【BZOJ】3479: [Usaco2014 Mar]Watering the Fields(kruskal)
http://www.lydsy.com/JudgeOnline/problem.php?id=3479 这个还用说吗.... #include <cstdio> #include < ...
- centos7 禁用每次sudo 需要输入密码
安装完centos7后,默认没有启用sudo,首先应该是对sudo进行设置.sudo的作用就是使当前非root用户在使用没有权限的命令 时,直接在命令前加入sudo,在输入自己当前用户的密码就可以完成 ...