C++primer 第 3 章 字符串、向量和数组 3 . 4 迭代器介绍

3.4迭代器介绍

  • 我们已经知道可以使用下标运算符来访问string对象的字符或vector对象的元素,还有另外一种更通用的机制也可以实现同样的目的,这就是迭代器(iterator)。在第II部分中将要介绍,除了vector之外,标准库还定义了其他几种容器。所有标准库容器都可以使用迭代器,但是其中只有少数几种才同时支持下标运算符。严格来说,string对象不属于容器类型,但是string支持很多与容器类型类似的操作。vector支持下标运算符,这点和string-样;string支持迭代器,这也和vector是一样的。类似于指针类型(参见2.3.2节,第47页),迭代器也提供了对对象的间接访问。就迭代器而言,其对象是容器中的元素或者string对象中的字符。使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另外一个元素。迭代器有有效和无效之分,这一点和指针差不多。有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置:其他所有情况都属于无效。

3.4.1使用迭代器

  • 和指针不一样的是,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员。比如,这些类型都拥有名为begin和end的成员,其中begin成员负责返回指向第一个元素(或第一个字符)的迭代器。如有下述语句:
  • / / 由编译器决定b 和 e 的类型;参见2.5.2节 (第 61页 )
  • // b 表示v 的第一个元素,e 表示v 尾元素的下一位置
  • auto b = v.begin () , e = v.end () ; //b 和 e 的类型相同
  • end成员则负责返回指向容器(或string对象)“尾元素的下一位置(onepasttheend)”的迭代器,也就是说,该迭代器指示的是容器的一个本不存在的“尾后(offtheend)”元素。这样的迭代器没什么实际含义,仅是个标记而已,表示我们已经处理完了容器中的所有元素。end成员返旧]的迭代器常被称作尾后迭代器(off^the-enditerator)或者简称为尾迭代器(enditerator特殊情况下如果容器为空,则begin和end返回的是同一个迭代器。
  • 如果容器为空,则begin和 end返回的是同一个迭代器,都是尾后迭代器。 
  • 一般来说,我们不清楚(不在意)迭代器准确的类型到底是什么。在上面的例子中,使用auto关键字定义变量b 和 e (参见2.5.2节,第 61页),这两个变量的类型也就是 begin 和 end的返回值类型,第 97页将对相关内容做更详细的介绍。

迭代器运算符

  • 表3.6列举了迭代器支持的一些运算。使用==和=来比较两个合法的迭代器是否相等,如果两个迭代器指向的元素相同或者都是同一个容器的尾后迭代器,则它们相等;否则就说这两个迭代器不相等

  • iter->mem  等效于 (*item).mem
  • 指针类似,也能通过解引用迭代器来获取它所指示的元素,执行解引用的迭代器必须合法并确实指示着某个元素(参见2.3.2节,第48页)。试图解引用一个非法迭代器或者尾后迭代器都是未被定义的行为。举个例子,3.2.3节(第84页)中的程序利用下标运算符把string对象的第一个字母改为了大写形式,下面利用迭代器实现同样的功能:

  • 本例和原来的程序一样,首先检查S 是否为空,显然通过检查begin和 end返回的结果 是否一致就能做到这一点。如果返回的结果一样,说明s为空;如果返回的结果不一样, 说明s不为空,此时s 中至少包含一个字符。 我们在if内部,声明了一个迭代器变量it并把begin返回的结果赋给它,这样就得到了指示s 中第一个字符的迭代器,接下来通过解引用运算符将第一个字符更改为大写 形式。

将迭代器从一个元素移动到另外一个元素

  • 迭代器使用递增(++)运算符(参见1.4.1节,第11页)来从一个元素移动到下一个元素。从逻辑上来说,迭代器的递增和整数的递增类似,整数的递增是在整数值上“加1”,迭代器的递增则是将迭代器“向前移动一个位置”。
  • 因为end返回的迭代器并不实际指示某个元素,所以不能对其进行递增或解 引用的操作

  • 和 3.2.3节 (第 84页)的那个程序一样,上面的循环也是遍历s 的字符直到遇到空白字符 为止,只不过之前的程序用的是下标运算符,现在这个程序用的是迭代器。
  • 循环首先用s .begin的返回值来初始化it,意味着it指示的是s 中的第一个字符 (如果有的话)。条件部分检查是否已到达s 的尾部,如果尚未到达,则将it解引用的结果传入isspace函数检查是否遇到了空白。每次迭代的最后,执行++it令迭代器平移 一个位置以访问s 的下一个字符。 循环体内部和上一个程序if语句内的最后一句话一样,先解引用it,然后将结果传入 toupper函数得到该字母对应的大写形式,再把这个大写字母重新赋值给it所指示的字符。

  • for循环 使用 != 进行比较,<这个支持的没有!= 广泛

迭代器类型

  • 就像不知道string和vector的size_type成员(参见3.2.2节,第79页)到底是什么类型一样,一般来说我们也不知道(其实是无须知道)迭代器的精确类型。而实际上,
    那些拥有迭代器的标准库类型使用iterator和const_iterator来表示迭代器的类型:
  • iterator  参考链接

  • const_iterator和常量指针(参见2.4.2节,第56页)差不多,能读取但不能修改它所指的元素值。相反iterator的对象可读可写。如果vector对象或string对象是-个常量,只能使用const_iterator;如果vector对象或string对象不是常量,那么既能使用iterator也能使用const_iterator

begin和end运算符

  • begin和end返回的具体类型由对象是否是常量决定,如果对象是常量,begin和end返回const_iterator;如果对象不是常量,返回iterator:
  • vector<int>v;

  • 有时候这种默认的行为并非我们所要。在6.2.3节(第191页)中将会看到,如果对象只需读操作而无须写操作的话最好使用常量类型(比如const_iterator)。为了便于专门得到const_iterator类型的返回值,C++11新标准引入了两个新函数,分别是cbegin和cend:
  • auto it3 = v . cbegin () ; // it3 的类型是 vector<int>: : const_iterator
  • 类似于begin和end,上述两个新函数也分别返回指示容器第一个元素或最后元素下一位置的迭代器。有所不同的是,不论vector对象(或string对象)本身是否是常量,返回值是const_iterator

结合解引用和成员访问操作

  • 解引用迭代器可获得迭代器所指的对象,如果该对象的类型恰好是类,就有可能希望进一步访问它的成员。例如,对于一个由字符串组成的vector对象来说,要想检查其元素是否为空,令it是该vector对象的迭代器,只需检查it所指字符串是否为空就可以了,其代码如下所示:
  • (*it).empty()
  • 注意,(*it).empty()中的圆括号必不可少,具体原因将在4.1.2节(第121页)介绍,该表达式的含义是先对it解引用,然后解引用的结果再执行点运算符(参见1.5.2节,第20页)。如果不加圆括号,点运算符将由it来执行,而非it解引用的结果:

  • 上面第二个表达式的含义是从名为it的对象中寻找其em pty成员,显然it是一个迭代 器,它没有哪个成员是叫empty的,所以第二个表达式将发生错误。 
  • 为了简化上述表达式,C++语言定义了箭头运算符(->)箭头运算符把解引用和成员访问两个操作结合在一起,也就是说,it->mem和表达的意思相同。例如,假设用一个名为text的字符串向量存放文本文件中的数据,其中的元素或者是一句话或者是一个用于表示段落分隔的空字符串。如果要输出text中第一段的内容,可以利用迭代器写一个循环令其遍历text,直到遇到空字符串的元素为止:

  • 我们首先初始化it令其指向text的第-个元素,循环重复执行直至处理完了text的所有元素或者发现某个元素为空。每次迭代时只要发现还有元素并且尚未遇到空元素,就输出当前正在处理的元素。值得注意的是,因为循环从头到尾只是读取text的元素而未向其中写值,所以使用了cbegin和cend来控制整个迭代过程。

某些对vector对象的操作会使迭代器失效

  • 3.3.2节 (第 90页)曾经介绍过,虽然vector对象可以动态地增长,但是也会有一些副作用。已知的一个限制是不能在范围for循环中向vector对象添加元素。另外一个限制是任何一种可能改变vector对象容量的操作,比如push_back,都会使该vector对象的迭代器失效。9.3.6节(第315页)将详细解释迭代器是如何失效的。迭代器 是指end 和 cend尾后迭代器
  • 谨记,但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素

3.4.2迭代器运算

  • 迭代器的递增运算令迭代器每次移动一个元素,所有的标准库容器都有支持递增运算的迭代器。类似的,也能用==和=对任意标准库类型的两个有效迭代器(参见3.4节,第95页)进行比较。string和vector的迭代器提供了更多额外的运算符,一方面可使得迭代器的每次移动跨过多个元素,另外也支持迭代器进行关系运算。所有这些运算被称作迭代器运算>其细节由表3.7列出。

迭代器的算术运算

  • 可以令迭代器和一个整数值相加(或相减),其返回值是向前(或向后)移动了若干个位置的迭代器。执行这样的操作时,结果迭代器或者指示原vector对象(或 string 对象)内的一个元素,或者指示原vector对象 (或 string对象)尾元素的下一位置。
  • 举个例子,下面的代码得到一个迭代器,它指向某vector对象中间位置的元素:
  • auto mid = vi.begin() + vi.size() / 2;   / / 计算得到最接近vi中间元素的一个迭代器
  • 如果vi有20个元素,vi.size()/2得10,此例中即令mid等于vi.begin()+10。已知下标从0开始,则迭代器所指的元素是vi[10],也就是从首元素开始向前相隔10个位置的那个元素。
  • 只要两个迭代器指向的是同一个容器中的元素或者尾元素的下一位置,就能将其相减,所得结果是两个迭代器的距离。所谓距离指的是右侧的迭代器向前移动多少位置就能追上左侧的迭代器,其类型是名为difference_type的带符号整型数。string和vector都定义了difference_type,因为这个距离可正可负,所以difference_type是带符号类型的。

使用迭代器运算

  • 使用迭代器运算的一个经典算法是二分搜索。二分搜索从有序序列中寻找某个给定的值。二分搜索从序列中间的位置开始搜索,如果中间位置的元素正好就是要找的元素,搜索完成;如果不是,假如该元素小于要找的元素,则在序列的后半部分继续搜素;假如该元素大于要找的元素,则在序列的前半部分继续搜索。在缩小的范围中计算一个新的中间元素并重复之前的过程,直至最终找到目标或者没有元素可供继续搜索。

  • 程序的一开始定义了三个迭代器:beg指向搜索范围内的第一个元素、end指向尾元素的下一位置、mid指向中间的那个元素。初始状态下,搜索范围是名为text的vector<string>的全部范围。
  • 循环部分先检查搜索范围是否为空,如果mid和end的当前值相等,说明已经找遍了所有元素。此时条件不满足,循环终止。当搜索范围不为空时,可知mid指向了某个元素,检查该元素是否就是我们所要搜索的,如果是,也终止循环。
  • 当进入到循环体内部后,程序通过某种规则移动beg或者end来缩小搜索的范围。如果mid所指的元素比要找的元素sought大,可推测若text含有sought,则必出现在mid所指元素的前面。此时,可以忽略mid后面的元素不再查找,并把mid赋给end即可。另一种情况,如果*mid比soughtZJS>则要找的元素必出现在mid所指兀素的后面。此时,通过令beg指向mid的下一个位置即可改变搜索范围。因为已经验证过mid不是我们要找的对象,所以在接下来的搜索中不必考虑它。
  • 循环过程终止时,mid或者等于end或者指向要找的元素。如果mid等于end,说明text中没有我们要找的元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/446665.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

英语口语-文章朗读Week9 TuesDay

朗读文章 People living in ancient times had no alternative but to do housework manually. They fire the wood when they cook,they hand wash clothes with hands; they sweep the floor with brooms. Now, modern inventions come as a great relief to people. We co…

C++primer 第 3 章 字符串、向量和数组 3 . 5 数组

3.5数组 数组是一种类似于标准库类型vector&#xff08;参见3.3节&#xff0c;第86页&#xff09;的数据结构&#xff0c;但是在性能和灵活性的权衡上又与vector有所不同。与vector相似的地方是&#xff0c;数组也是存放类型相同的对象的容器&#xff0c;这些对象本身没有名字…

C++primer 第 4 章 表达式 4.1基础 4 . 2 算术运算符 4 .3 逻辑和关系运算符 4 . 4 赋值运算符 4 .5 递增和递减运算符 4.6成员访问运算符

表达式由一个或多个运算对象(operand)组成&#xff0c;对表达式求值将得到一个结果(result)字面值和变量是最简单的表达式(expression),其结果就是字面值和变量的值。把一个运算符(operator)和一个或多个运算对象组合起来可以生成较复杂的表达式 4.1基础 有几个基础概念对表达…

codeforces 266B-C语言解题报告

266B题目网址 题目解析 输入n,t,排队情况s,输出第t次循环后,排队情况 举例: 输入: 5 1 BGGBG 输出: GBGGB 2.输入的n代表排队的人数,t代表整个循环t次之后再输出结果 3.注意点: 使用while()大循环去控制t次的循环,使用for()内层循环去遍历整个字符串 如果if(s[j]‘B’&…

英语口语-文章朗读Week9 Wednesday

英语文章 Birds of the same species flock together&#xff0c; People tend to look for someone like themselves to be friends. But having the same interests is not the only standard when we are seeking friends. In most cases, especially for adults, people l…

C++primer 第 4 章 表达式 4.7条件运算符 4.8位运算符 4.9 sizeof运算符 4.10逗号运算符 4.11类型转换 4 . 1 2 运算符优先级表

4.7条件运算符 条件运算符(?&#xff1a;)允许我们把简单的if else逻辑嵌入到单个表达式当中&#xff0c;条件运算符按照如下形式使用&#xff1a;cond ? expr1 : expr2;其中cond是判断条件的表达式&#xff0c;而expr1和expr2是两个类型相同或可能转换为某个公共类型的表达…

Git 之 git tag标签使用

目录一、简介二、本地tag操作1、创建tag标签&#xff08;1&#xff09;创建轻量标签&#xff08;2&#xff09;创建附注标签2、查看tag标签&#xff08;1&#xff09;查看标签列表&#xff08;2&#xff09;查看标签提交信息&#xff08;3&#xff09;在提交历史中查看标签3、删…

C++primer 第 5 章语句 5.2语句作用域 5.3条件语句 5 . 4 迭代语句 5.5跳转语句 5.6 try语句块和异常处理

5 . 1 简单语句 C语言中的大多数语句都以分号结束&#xff0c;一个表达式&#xff0c;比如ival 5 , 末尾加上分号就变成了表达式语句(expression statement)。表达式语句的作用是执行表达式并丢弃掉求值结果&#xff1a;ival 5&#xff1b; // 一条没什么实际用处的表达式语…

英语口语-文章朗读Week9Thursday

英语文章 Everyone has his or her own dreams. Some people wants to be millionaires so they can give many generous donations later; some people want to be scientists so they can bring many conveniences to the world; some people only want to be bus-drivers s…

操作系统 内存管理相关知识

cpu执行程序的基本过程 译码器 输入为n管脚&#xff0c;输出为2^n根管脚&#xff0c;编号为从0到2^(n-1)&#xff0c;用少的输入端控制更多的输出端最常用的是三八译码器AD(Address bus)地址总线: 选中一行数据每一行 8bit 组成8吧B cpu输入端32根线&#xff0c;输出端就可以控…

Chrome浏览器必装插件!尤其程序猿!

Chrome 浏览器有一个好处&#xff0c;就是插件极其丰富&#xff0c;只有你想不到的&#xff0c;没有你找不到的&#xff0c;这恐怕是 Chrome 浏览器被众多爱好者钟爱的原因吧。 言归正传&#xff0c;今天来给大家推荐 10 款我自己珍藏的 Chrome 浏览器插件。 1、crxMouse Ch…

英语口语-文章朗读Week10 Monday

英语文章 Here are some valuable suggestions which may assist you in landing good job First, make your resume clear and associate it with the position you are applying for. Try to add details like your temporary jobs at college or your former jobs Second, …

英语口语-文章朗读Week10 Wednesday

英语文章 Everyone needs sleep for survival, but how much? It is said that eight hours of sleep is fundamental to a healthy person. But today, many people are sacrificing their sleep time。 Modern people have so many alternatives: cell phones, PCs, TVs, g…

嵌入式Linux多任务编程 进程 管道 命名管道

进程 进程是一个可并发执行的具有独立功能的程序关于某个数据集合的一次执行过程&#xff0c;也是操作系统执行资源分配和保护的基本单位。程序的一次执行就是一个进程一个程序可以派生多个进程多个不同程序运行的时候&#xff0c;也会有多个相对应的进程与其相互对应进程是动…

英语口语-文章朗读Week10 Thursday

英语文章 There are many customs and traditions in Chinese civilization. Here, we will talk about the development of the way people greet each other: In ancient times, people had to kneel to those who were superior to them. This custom remained until the …

Linux进程之间通信 信号

2) SIGINT 程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出&#xff0c;用于通知前台进程组终止进程。 3) SIGQUIT 和SIGINT类似, 但由QUIT字符(通常是Ctrl-\)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。 15)…

Linux进程之间通信 消息队列

使用命令 ipcs -q 查看对应的消息队列代码 文件接收者 #include <sys/types.h> #include <stdio.h> #include <unistd.h> #include <string> #include <signal.h> #include <wait.h> #include <sys/msg.h> #include <cstring&g…

c++面向对象高级编程 学习二 带指针的类

带指针的类&#xff0c;必须要自己写拷贝构造和赋值构造 拷贝构造&#xff1a;参数和类的类型一样的构造函数 赋值构造&#xff1a;重写操作符&#xff0c;且其参数和类的类型一样 class String { public: String(const char* cstr 0); String(const String& str); Strin…

英语口语 week11 Tuesday

英语文章 It was a cold and gloomy winter afternoon, people with their chilled hands tucked into their pockets or hidden in their sleeves. Fred was in a depressed mood, just like the weather,for he failed to get any award in the debate competition When he …