C++中的迭代器
“指针”对所有C/C++的程序员来说,一点都不陌生。在接触到C语言中的malloc函数和C++中的new函数后,我们也知道这两个函数返回的都是一个指针,该指针指向我们所申请的一个“堆”。提到“堆”,就不得不想到“栈”,从C/C++程序设计的角度思考,“堆”和“栈”最大的区别是“栈”由系统自动分配并且自动回收,而“堆”则是由程序员手动申请,并且显示释放。如果程序员不显示释放“堆”,便会造成内存泄漏,内存泄漏的危害大家知道,严重时会导致系统崩溃。
既然“指针”的使用者一不小心就可能导致内存泄漏,那么我们如何能够使得指针的使用变得更安全呢?从C++面向对象的角度分析,我们有没有可能将“指针”封装起来,使得用户不直接接触指针,而使用一个封装后的对象来替代指针的操作呢?
答案是显然的,“智能指针”(smart pointer)正解决这类问题,尤其是在防止内存泄漏方面做得非常突出。
迭代器iterator就是一种智能指针,它对原始指针进行了封装,并且提供一些等价于原始指针的操作,做到既方便又安全。
简单来说就是提供一种方法,在不需要暴露某个容器的内部表现形式情况下,使之能依次访问该容器中的各个元素,这种设计思维在STL中得到了广泛的应用,是STL的关键所在,通过迭代器,容器和算法可以有机的粘合在一起,只要对算法给予不同的迭代器,就可以对不同容器进行相同的操作。
尽管我们可以使用下标来访问字符串中的字符或vector的元素,但更一般的机制是使用迭代器(iterator)。
所有的容器都支持迭代器,但仅少数几个支持下标操作。可以替代下标访问vector对象的元素。
合法的迭代器:
- 指示某个元素
- 指示最后一个元素的下一个位置
- 其它的迭代器都是不合法的。
- 使用迭代器
迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围。迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器不仅仅是指针,因此你不能认为他们一定具有地址值。例如,一个数组索引,也可以认为是一种迭代器。
迭代器有各种不同的创建方法。程序可能把迭代器作为一个变量创建。一个STL容器类可能为了使用一个特定类型的数据而创建一个迭代器。作为指针,必须能够使用*操作符类获取数据。你还可以使用其他数学操作符如++。典型的,++操作符用来递增迭代器,以访问容器中的下一个对象。如果迭代器到达了容器中的最后一个元素的后面,则迭代器变成past-the-end值。使用一个past-the-end值得指针来访问对象是非法的,就好像使用NULL或为初始化的指针一样。
提示:
STL不保证可以从另一个迭代器来抵达一个迭代器。例如,当对一个集合中的对象排序时,如果你在不同的结构中指定了两个迭代器,第二个迭代器无法从第一个迭代器抵达,此时程序注定要失败。这是STL灵活性的一个代价。
每种容器类型都定义了自己的迭代器类型,如 vector:
vector<int>::iterator iter;
语句定义了一个名为 iter 的变量,它的数据类型是 vector<int> 定义的 iterator 类型。每个标准库容器类型都定义了一个名为 iterator 的成员,这里的 iterator 与迭代器实际类型的含义相同。
begin 和 end 操作
vector<int>::iterator iter = ivec.begin();
上述语句把 iter 初始化为由名为 vector 操作返回的值。假设 vector 不空,初始化后,iter 即指该元素为 ivec[0]。由 end 操作返回的迭代器指向 vector 的“末端元素的下一个”。表明它指向了一个不存在的元素。如果 vector 为空,begin 返回的迭代器与 end 返回的迭代器相同。由 end 操作返回的迭代器并不指向 vector 中任何实际的元素,相反,它只是起一个哨兵(sentinel)的作用,表示我们已处理完 vector 中所有元素。
vector 迭代器的自增和解引用运算
*iter = 0;
解引用操作符返回迭代器当前所指向的元素。假设 iter 指向 vector 对象 ivec 的第一元素,那么 *iter 和 ivec[0] 就是指向同一个元素。上面这个语句的效果就是把这个元素的值赋为 0。
迭代器使用自增操作符向前移动迭代器指向容器中下一个元素。从逻辑上说,迭代器的自增操作和 int 型对象的自增操作类似。对 int 对象来说,操作结果就是把 int 型值“加 1”,而对迭代器对象则是把容器中的迭代器“向前移动一个位置”。因此,如果 iter 指向第一个元素,则 ++iter 指向第二个元素
由于 end 操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。
迭代器的其他操作
迭代器应用的程序示例
使用迭代器下标、迭代器改变vector的内容
#include <iostream>
#include <string>
#include <vector> int print_int_vector(std::vector<int> ivec)
{
for(std::vector<int>::size_type ix =0, j = 0; ix != ivec.size(); ++ix, ++j)
{
std::cout<<ivec[ix]<<" "; //加空格!
}
std::cout<<std::endl;
return 0;
} int main()
{
std::vector<int> ivec(10, 68); // empty vector
print_int_vector(ivec);
// reset all the elements in ivec to 0
/*
// 使用下标
for (std::vector<int>::size_type ix = 0; ix != ivec.size(); ++ix)
{
ivec[ix] = 0;
}
*/
// equivalent loop using iterators to reset all the elements in ivec to 0
for (std::vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter)
*iter = 0; // set element to which iter refers to 0
print_int_vector(ivec);
return 0;
}
const_iterator
C++为每种容器类型定义了一种名为const_iterator的类型,该类型只能用于读取容器内的元素,但不能改变其值。
对const_iterator类型解引用,得到的是一个指向const对象的引用。
for (vector<string>::const_iterator iter = text.begin(); iter != text.end(); ++ iter){
cout << *iter << endl; //ok: print each element in text
*iter = " "; // error: *iter is const
}
const_iterator可以用于const或者非const容器(因为不能修改对象的值),但是const的iterator只能用于非const容器(只能修改唯一指向的值)。
不要把 const_iterator 对象与 const 的 iterator 对象混淆起来。声明一个 const 迭代器时,必须初始化迭代器。一旦被初始化后,就不能改变它的值
vector<int> nums(10); // nums is nonconst
const vector<int>::iterator cit = nums.begin();
*cit = 1; // ok: cit can change its underlying element
++cit; // error: can't change the value of cit
总结
迭代器的算术操作
*iter = i; // set element to which iter refers to i
ivec.push_back(i*2);
加上这句代码没问题,正确运行,但是,我们试图在for循环里面执行,即
{
*iter = i; // set element to which iter refers to i
ivec.push_back(i*2);
}
则会莫名其妙退出!
参考博客:
http://lib.csdn.net/article/cplusplus/25895
http://blog.csdn.net/jxh_123/article/details/30793397?utm_source=tuicool&utm_medium=referral
http://blog.csdn.net/xxin_w/article/details/20551939
http://www.cnblogs.com/besty/p/4039264.html
http://ceeji.net/blog/cpp-learn-iterator/
http://blog.csdn.net/zhanh1218/article/details/33340959
http://blog.csdn.net/u014800748/article/details/47401429
感谢The_Third_Wave
C++中的迭代器的更多相关文章
- ES6中的迭代器(Iterator)和生成器(Generator)
前面的话 用循环语句迭代数据时,必须要初始化一个变量来记录每一次迭代在数据集合中的位置,而在许多编程语言中,已经开始通过程序化的方式用迭代器对象返回迭代过程中集合的每一个元素 迭代器的使用可以极大地简 ...
- python is、==区别;with;gil;python中tuple和list的区别;Python 中的迭代器、生成器、装饰器
1. is 比较的是两个实例对象是不是完全相同,它们是不是同一个对象,占用的内存地址是否相同 == 比较的是两个对象的内容是否相等 2. with语句时用于对try except finally 的优 ...
- JAVA集合中的迭代器的遍历
JAVA中的迭代器,迭代实质上就是遍历,在JAVA中使用iterator()方法进行迭代.需要注意的是,iterator()方法的返回值是Iterator对象.Iterator对象有三个方法,hasN ...
- python中的迭代器详解
#原创,转载请先联系 理论性的东西有点枯燥,耐心点看- 1.迭代是什么? 我们知道可以对list,tuple,dict,str等数据类型使用for...in的循环语法,从其中依次取出数据,这个过程叫做 ...
- 牛客网Java刷题知识点之Java 集合框架的构成、集合框架中的迭代器Iterator、集合框架中的集合接口Collection(List和Set)、集合框架中的Map集合
不多说,直接上干货! 集合框架中包含了大量集合接口.这些接口的实现类和操作它们的算法. 集合容器因为内部的数据结构不同,有多种具体容器. 不断的向上抽取,就形成了集合框架. Map是一次添加一对元素. ...
- ES6中的迭代器(Iterator)和生成器(Generator)(一)
用循环语句迭代数据时,必须要初始化一个变量来记录每一次迭代在数据集合中的位置,而在许多编程语言中,已经开始通过程序化的方式用迭代器对象返回迭代过程中集合的每一个元素 迭代器的使用可以极大地简化数据操作 ...
- mapreduce中reduce中的迭代器只能调用一次。其实迭代器就只能调用一次
亲测,只能调用一次,如果想想在一次reduce重复使用迭代器中的数据,得先取出来放在list中然后在从list中取出来!!多次读取reduce函数中迭代器的数据 public static void ...
- ES6中的迭代器、Generator函数以及Generator函数的异步操作
最近在写RN相关的东西,其中涉及到了redux-saga ,saga的实现原理就是ES6中的Generator函数,而Generator函数又和迭代器有着密不可分的关系.所以本篇博客先学习总结了ite ...
- pytorch :: Dataloader中的迭代器和生成器应用
在使用pytorch训练模型,经常需要加载大量图片数据,因此pytorch提供了好用的数据加载工具Dataloader. 为了实现小批量循环读取大型数据集,在Dataloader类具体实现中,使用了迭 ...
随机推荐
- (转载)TCP/IP的三次握手与四次挥手
TCP三次握手 所谓三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送3个包. 三次握手的目的是连接服务器指定端口,建立TCP连接,并同步 ...
- PyCharm配置GitHub
原文出处: https://github.com/wssnail/ws96apt/blob/master/weixin/a.py#L21-21打开file,选择settings,找到Version C ...
- IP和端口的相关检测
1.查看自己电脑的ip,使用ipconfig命令 2.检测某个ip是否可以连通,直接使用ping命令 3.windows下查看本机都有哪些端口开放,使用netstat -anp tcp 命令 4.查看 ...
- Google Protocol Buffer的安装与.proto文件的定义
什么是protocol Buffer呢? Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准. 我理解的就是:它是一种轻便高效的结构 ...
- angularJS学习笔记之——搭建学习环境
学习AngularJS已经好几天了,从今天开始学习AngularJS环境搭建. 无论是Mac.Linux或Windows环境中,您均可遵循本教程学习编程. 第一步:安装Git Git是什么呢? Git ...
- D3.js 做一个简单的图表(条形图)
柱形图是一种最简单的可视化图标,主要有矩形.文字标签.坐标轴组成. 本文为简单起见,只绘制矩形的部分,用以讲解如何使用 D3 在 SVG 画布中绘图. 一. 画布是什么 前几章的处理对象都是 HTML ...
- ToolStrip添加自定义的DateTimePicker
直接新建一个类,代码如下: [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.All), DefaultEven ...
- C/C++-----------http协议发送字段,文件,单个和多张图片
关于c/c++ 网络编程,无论在linux还是windows,要说到自由性,和安全性,socket无疑是比较好的!对于socket,因为它的传输协议只有两种tcp和udp,属于网络层,这里我们不去重点 ...
- java selenium 题目二 如何处理Table
如果操作Table. 例如我有一个table. table 的列的顺序是会变化的, 行数也是会变化的. 问题: 如果通过名字获取到年龄 HTML 源代码 <html> <body ...
- WEB UI 整理
当下对于网站前段开发人员来说,很少有人不使用一些JS框架或者WEB UI库,因此这些可以有效提高网站前段开发速度,并且能够统一开发环境,对于不同浏览器的兼容性也不需要程序员操心,有了这些优点,当然大家 ...