迭代器是一种检查容器内元素并遍历元素的数据类型。可以替代下标访问vector对象的元素。

每种容器类型都定义了自己的迭代器类型,如 vector:

  1. vector<int>::iterator iter;

这符语句定义了一个名为 iter 的变量,它的数据类型是 vector<int> 定义的 iterator 类型。每个标准库容器类型都定义了一个名为 iterator 的成员,这里的 iterator 与迭代器实际类型的含义相同。

begin 和 end 操作

每种容器都定义了一对命名为 begin 和 end 的函数,用于返回迭代器。如果容器中有元素的话,由 begin 返回的迭代器指向第一个元素:
  1. vector<int>::iterator iter = ivec.begin();

上述语句把 iter 初始化为由名为 vector 操作返回的值。假设 vector 不空,初始化后,iter 即指该元素为 ivec[0]。由 end 操作返回的迭代器指向 vector 的“末端元素的下一个”。表明它指向了一个不存在的元素。如果 vector 为空,begin 返回的迭代器与 end 返回的迭代器相同。由 end 操作返回的迭代器并不指向 vector 中任何实际的元素,相反,它只是起一个哨兵(sentinel)的作用,表示我们已处理完 vector 中所有元素。

【备注:不用担心begin和end在循环中的条件判断。大胆使用吧!】

vector 迭代器的自增和解引用运算

迭代器类型可使用解引用操作符(dereference operator)(*)来访问迭代器所指向的元素:

  1. *iter = 0;

解引用操作符返回迭代器当前所指向的元素。假设 iter 指向 vector 对象 ivec 的第一元素,那么 *iter 和 ivec[0] 就是指向同一个元素。上面这个语句的效果就是把这个元素的值赋为 0。
迭代器使用自增操作符向前移动迭代器指向容器中下一个元素。从逻辑上说,迭代器的自增操作和 int 型对象的自增操作类似。对 int 对象来说,操作结果就是把 int 型值“加 1”,而对迭代器对象则是把容器中的迭代器“向前移动一个位置”。因此,如果 iter 指向第一个元素,则 ++iter 指向第二个元素。

由于 end 操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。

迭代器的其他操作

另一对可执行于迭代器的操作就是比较:用 == 或 != 操作符来比较两个迭代器,如果两个迭代器对象指向同一个元素,则它们相等,否则就不相等。

迭代器应用的程序示例

1、使用迭代器和下标改变vector的内容

这个很简单,请看代码。
  1. #include <iostream>
  2. #include <string>
  3. #include <vector>
  4. int print_int_vector(std::vector<int> ivec)
  5. {
  6. for(std::vector<int>::size_type ix =0, j = 0; ix != ivec.size(); ++ix, ++j)
  7. {
  8. std::cout<<ivec[ix]<<" "; //加空格!
  9. }
  10. std::cout<<std::endl;
  11. return 0;
  12. }
  13. int main()
  14. {
  15. std::vector<int> ivec(10, 68); // empty vector
  16. print_int_vector(ivec);
  17. // reset all the elements in ivec to 0
  18. /*
  19. // 使用下标
  20. for (std::vector<int>::size_type ix = 0; ix != ivec.size(); ++ix)
  21. {
  22. ivec[ix] = 0;
  23. }
  24. */
  25. // equivalent loop using iterators to reset all the elements in ivec to 0
  26. for (std::vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter)
  27. *iter = 0; // set element to which iter refers to 0
  28. print_int_vector(ivec);
  29. return 0;
  30. }

2、tuple功能的实现【不可变性】

const_iterator类型只能用于读取容器内元素,但不能改变其值。
当我们对普通 iterator 类型解引用时,得到对某个元素的非 const。而如果我们对 const_iterator 类型解引用时,则可以得到一个指向 const 对象的引用),如同任何常量一样,该对象不能进行重写。
如果使用const_itreator进行重写,编译时会报错!
使用 const_iterator 类型时,我们可以得到一个迭代器,它自身的值可以改变,但不能用来改变其所指向的元素的值。可以对迭代器进行自增以及使用解引用操作符来读取值,但不能对该元素赋值。
【注意:不要把 const_iterator 对象与 const 的 iterator 对象混淆起来。声明一个 const 迭代器时,必须初始化迭代器。一旦被初始化后,就不能改变它的值。】
  1. vector<int> nums(10); // nums is nonconst
  2. const vector<int>::iterator cit = nums.begin();
  3. *cit = 1; // ok: cit can change its underlying element
  4. ++cit; // error: can't change the value of cit

【注意:const_iterator 对象可以用于 const vector 或非 const vector,因为不能改写元素值。const 迭代器这种类型几乎没什么用处:一旦它被初始化后,只能用它来改写其指向的元素,但不能使它指向任何其他元素。】

tuple不可变的实现需要使用const声明!const vector<int> nums(10, 9);

总结

1、const_iterator需要注意:这个vector本身还是可变的,只不过对const_iterator类型解引用的对象不可变。
2、const迭代器也就是只能指向其所指向的元素,不能通过++等操作去指向其他元素。但是,所指向这个元素可以改变。
3、需要定义真正tuple,那就用const vector<int> nums(10, 9);来定义!此时,必须使用const_iterator 来获取每个元素的值。

迭代器的算术操作

1、可以对迭代器对象加上或减去一个整形值。这样做将产生一个新的迭代器,其位置在 iter 所指元素之前(加)或之后(减) n 个元素的位置。加或减之后的结果必须指向 iter 所指 vector 中的某个元素,或者是 vector 末端的后一个元素。加上或减去的值的类型应该是 vector 的 size_type 或 difference_type 类型,例子:
  1. int main()
  2. {
  3. std::vector<int> ivec(10, 68);
  4. print_int_vector(ivec);
  5. int i = 0;
  6. for (std::vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter, i++)
  7. *iter = i; // set element to which iter refers to i
  8. print_int_vector(ivec);
  9. std::vector<int>::iterator iter = ivec.begin();
  10. iter += 100;
  11. std::cout<<*iter;
  12. return 0;
  13. }

本例子中ivec有10个元素,iter+=j,j在10以内都不会有错(0~9),大于等于10则会出现溢出问题,编译器,运行中间都不会报错!可以加当然可以减。

2、iter1 - iter2:
该表达式用来计算两个迭代器对象的距离,该距离是名为 difference_type 的 signed 类型 size_type 的值,这里的 difference_type 是 signed 类型,因为减法运算可能产生负数的结果。该类型可以保证足够大以存储任何两个迭代器对象间的距离。iter1 与 iter2 两者必须都指向同一 vector 中的元素,或者指向 vector 末端之后的下一个元素。
3、可以用迭代器算术操作来移动迭代器直接指向某个元素,例如,下面语句直接定位于 vector 中间元素:
  1. vector<int>::iterator mid = vi.begin() + vi.size() / 2;

上述代码用来初始化 mid 使其指向 vi 中最靠近正中间的元素。这种直接计算迭代器的方法,与用迭代器逐个元素自增操作到达中间元素的方法是等价的,但前者的效率要高得多。

4、任何改变 vector 长度的操作都会使已存在的迭代器失效。例如,在调用 push_back 之后,就不能再信赖指向 vector 的迭代器的值了。
请看例子:
  1. *iter = i; // set element to which iter refers to i
  2. ivec.push_back(i*2);

加上这句代码没问题,正确运行,但是,我们试图在for循环里面执行,即:

  1. {
  2. *iter = i; // set element to which iter refers to i
  3. ivec.push_back(i*2);
  4. }

则会莫名其妙退出!

C++之STL迭代器的更多相关文章

  1. STL迭代器笔记

    STL迭代器简介 标准模板库(The Standard Template Library, STL)定义了五种迭代器.下面的图表画出了这几种: input         output \       ...

  2. 一步一步的理解C++STL迭代器

    一步一步的理解C++STL迭代器 "指针"对全部C/C++的程序猿来说,一点都不陌生. 在接触到C语言中的malloc函数和C++中的new函数后.我们也知道这两个函数返回的都是一 ...

  3. STL 迭代器 iterator const

    STL迭代器很多时候可以当成指针来使用. 但是指针一般可以用const来控制访问. 那迭代器呢. #include <iostream> #include <vector> u ...

  4. STL迭代器的使用、正向、逆向输出双向链表中的所有元素

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  5. 【STL 源码剖析】浅谈 STL 迭代器与 traits 编程技法

    大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub : https://github.com/rongweihe/Mor ...

  6. STL迭代器之一:偏特化

    在stl的算法中运用容器的迭代器时,很可能经常会用到迭代器相应型别(例如迭代器所指物的型别),假设算法中有必要声明一个变量,以"迭代器所指对象的型别"为类型,如何是好,例如我们写一 ...

  7. C++ STL 迭代器失效问题

    之前看<C++ Primier>的时候,也解到在顺序型窗口里insert/erase会涉及到迭代器失效的问题,并没有深究.今天写程序的时候遇到了这个问题. 1 莫名其妙的Erase 最初我 ...

  8. stl 迭代器(了解)

    STL 主要是由 containers(容器),iterators(迭代器)和 algorithms(算法)的 templates(模板)构成的. 对应于它们所支持的操作,共有五种 iterators ...

  9. stl迭代器原理

    具体实现肯定不如书上讲的清楚了,这里只是根据侯捷书上的讲解,自己建立一条思路以及形成一些相关的概念 迭代器也可被称作智能指针,用于遍历容器内的元素,stl每个容器都实现了自己的iterator,ite ...

  10. STL——迭代器的概念

    迭代器是一种抽象的设计概念,现实程序语言中并没有直接对应于这个概念的实物. 1 迭代器设计思维——STL关键所在 不论是泛型思维或STL的实际运用,迭代器都扮演这重要的角色.STL的中心思想在于:将数 ...

随机推荐

  1. POJ2386 Lake Counting 【DFS】

    Lake Counting Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 20782   Accepted: 10473 D ...

  2. binary-tree-level-order-traversal I、II——输出二叉树的数字序列

    I Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to righ ...

  3. linux实现php定时执行cron任务详解(转)

    对于PHP本身并没有一套解决方案来执行定时任务,不过是借助sleep函数完成的.这种方就是要提前做一些配置,如实现过程: 复制代码 代码如下: ignore_user_abort();//关掉浏览器, ...

  4. 【重磅干货】看了此文,Oracle SQL优化文章不必再看!

    目录 SQL优化的本质 SQL优化Road Map 2.1 制定SQL优化目标 2.2 检查执行计划 2.3 检查统计信息 2.4 检查高效访问结构 2.5 检查影响优化器的参数 2.6 SQL语句编 ...

  5. java 多线程2(转载)

    http://www.cnblogs.com/DreamSea/archive/2012/01/11/JavaThread.html Ø线程的概述(Introduction) 线程是一个程序的多个执行 ...

  6. 如果在 Code First 模式下使用,则使用 T4 模板为 Database First 和 Model First

    web.config里的链接字符串最好和app.config里相同,因为ef的链接字符串需要一些特殊的参数

  7. js中变量的声明

    大家都知道js中变量的声明是要提前的,下面有4个样例: 1.if(!"t" in window){  var t = 1; }       alert(t);答案是undefine ...

  8. iOS开发之NewsstandKit.framework的使用

    本文转载至 http://mobile.51cto.com/iphone-423385.htm   系统提供NewsstandKit.framework来支持newsstand类型的程序,就是在spr ...

  9. 在苹果iOS平台中获取当前程序进程的进程名等信息

    本文由EasyDarwin开源团队成员Penggy供稿: Objective-C 提供 NSProcessInfo 这个类来获取当前 APP 进程信息, 然而我们的静态库是 pure C++ 工程. ...

  10. vs2015编译EasyDarwin开源流媒体服务器Linux版本调研

    本文转自EasyDarwin团队成员Alex的博客:http://blog.csdn.net/cai6811376/article/details/51843196 之前InfoQ的一篇文章提到用vs ...