《C++ primer》英文第五版阅读笔记(十一)——Iterators

Chapter3,Section3.4 Iterators


迭代器


尽管我们能够使用下标去访问string或者vector里面的元素,我们也可以使用另一种方法来达到相同的目的——使用Iterators迭代器。除了vector,库里面定义了还定义了许多的容器。所有的容器都有迭代器,但是只有它们中的少数支持下标操作。从技术上讲,string并不是容器,但是它可以进行大多数容器可以做的操作。比如string像vector一样,有下标操作,也有迭代器。


(一)使用迭代器

和指针不同的是,我们不需要通过地址符来得到一个迭代器。包含有迭代器的类型都有一个能够返回迭代器的成员。特别地,这些类型都有名为begin和end的迭代器。begin成员(方法)返回一个表示第一个元素(或第一个字符)的迭代器。end方法返回的是表示容器(或)string的最后一个元素的下一个位置的迭代器,它具体表示容器末尾的一个不存在的元素。由end返回的迭代器通常被称为“结束迭代器(off-the-end Iterators)”。

如果容器是空的,begin和end返回的迭代器是相同的,它们返回的都是"结束迭代器"。

通常我们并不知道(关心)一个迭代器的具体类型。我们可以通过使用auto来进行类型的自动获取。


Iterator迭代器可以进行的操作如下:
*iter
返回由迭代器iter所表示的元素的引用。
iter->mem 给iter解引用,从根本的元素那里获得名为mem的成员。等价于(*iter).mem。
++iter 让iter自增,让它指向容器中的下一个元素。
--iter 让iter自减,让它指向容器中的前一个元素。
iter1 == iter2 比较两个迭代器是否相等。当两个迭代器相等时有两种情况:

iter1 !=iter21.两个迭代器表示同一个容器里的同一个元素。2.两个迭代器是同一个容器的结束迭代器。

和指针一样,我们可以通过给迭代器解引用来获取迭代器表示的元素。同时,和指针一样,我们也只能给一个有效的迭代器(能够表示一个元素)解引用。给一个无效的迭代器解引用或者给结束迭代器解引用得到的结果是undefined。


如果一迭代器的begin和end的返回值相同,那么这个迭代器就是空的。


迭代器使用自增++操作从一个元素移动到另一个元素。迭代器的自增和整型变量的自增是在逻辑上相似的。给一个整型自增,是使它的值加一;而给一个迭代器自增,是使它从一个位置移动到另一个位置。

因为由end返回的迭代器并不表示一个元素,所以它不能被解引用或进行自增操作。


在C++里面由于习惯,通常使用!=,而不是<;就像通常使用迭代器而不使用数组一样。

仅仅少数的库类型(比如vector和string)有下标操作。相似地,所有库里面的容器都有能够定义==和!=操作的迭代器。大多数的这些迭代器不含有<操作。通过使用迭代器和!=,我们就不用担心我们正在处理的容器是什么类型的了。


就像我们不必知道vector和string的size_type是什么类型一样,我们通常也不需要知道一个迭代器的具体类型。

和size_type类型一样,库类型里面定义了具体的迭代器类型——iterator和const_iterator

例:vector<int>::iterator v1;

vector<int>::const_iterator v2;

string::iterator v3;

string::const_iterator v4;

const_iterator类型的迭代器和const类型的指针一样,只能进行元素的读取,而不能修改它所指的元素的值。

iterator类型的迭代器既能进行元素的读取,也能进行元素的修改。

一个const类型的vector或string,它的迭代器只能是const_iterator类型的。

一个非const类型的vector或string,它的迭代器既能是const_iterator类型的,又能是iterator类型的。


术语iterator被用来指三种不同的实体:

1.可能是指迭代器的概念。

2.可能是指由容器定义的迭代器类型。

3.可能是指一个迭代器对象。


begin和end返回的类型取决于他们操作的对象是否是const的。

如果对象是const类型的,则begin和end返回一个const_iterator类型的迭代器。如果不是const的,则返回一个iterator。

但是通常这种默认的操作方式往往得不到想要的结果。当我们只读不写时,我们最好使用const类型(例如:const_iterator)。


新标准里面定义了两个新的函数来获取const_iterator类型的迭代器:cbegin和cend。

和begin与end一样,它们分别返回容器中的第一个元素和最后一个元素后面的位置。无论vector或string是否为const类型,他们都会返回一个const_iterator类型的迭代器。


当我们解引用一个iterator时,我们得到的是迭代器表示的对象。如果这个对象是类类型,我们就可以访问这个类里面的成员。比如我们可能要检验一个vector里面的strings是否是空的。如果it表示vector<string>的一个迭代器,我们要检验it所表示的strings是否为空。可以这样写:(*it).empty()((it*)里面的小括号是必须的。)我们也可以采用简化的写法。上面写法的等价式子是:it->empty()。


vector能够动态的进行变化。有一些操作实现时是需要注意的:

1.我们不能在range for循环体里面给vector添加元素。

2.任何改变了vector大小的操作,比如push_back,会潜在地使这个vector失效。


注意:使用了迭代器的循环里面不能给该迭代器所指的容器里面添加元素。


(二)迭代器的算术运算

strings和vector类型的迭代器支持一些额外的迭代器操作。比如它们能让迭代器一次移动多个位置。它们也支持所有的关系运算符。这些操作被称为“迭代器的算术运算”。

strings和vector迭代器支持的操作如下:

以下操作返回的迭代器必须表示同一个容器里面的元素或者是相关的容器(vector或string)末尾的后一个位置。

iter + n 返回iter之后的第n个元素。

iter - n 返回iter之前的第n个元素

iter1 += n 向后跳n个元素。

iter1 -= n 向前跳n个元素。

iter1 - iter2 返会iter1和iter2之间的距离。

结果是有符号整数的类型difference_type。vector和string里面都定义了这个类型,这个类型是有符号的。因为相减的结果可能是负的。

>,>=,<,<= 一个迭代器小于另一个迭代器,如果它所指的容器中的元素出现在另一个迭代器所指的相同的元素之前。?迭代器必须表示同一个容器里面的元素或者是相关的容器末尾的后一个位置。

进行比较时,迭代器必须是有效的,并且表示同一个vector或string里面的元素(末尾的下一个位置)。

(<判断一个迭代器是否在另一个迭代器前面。)


一个典型的应用迭代器算法的例子是二分法。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值