迭代器介绍

我们已经知道可以使用下标运算符来访问string对象的字符或vector对象的元素,还有另外一种更通用的机制也可以实现同样的目的,这就是迭代器。所有标准库容器都可以使用迭代器,但是其中只有少数几种才同时支持下标运算符。严格来说,string对象不属于容器类型,但是string支持很多与容器类型类似的操作。vector支持下标运算符,这点和string一样;string支持迭代器,这也和vector是一样的。

类似于指针类型,迭代器也提供了对对象的间接访问。就迭代器而言,其对象是容器中的元素或者string对象中的字符。使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另一个元素。迭代器有有效和无效之分,这一点和指针差不多。有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一个位置;其他所有情况都属于无效。

 

使用迭代器

和指针不一样的是,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员。比如,这些类型都拥有名为begin和end的成员,其中begin成员负责返回指向第一个元素(或第一个字符)的迭代器。如有下述语句:

//由编译器决定b和e的类型;

//b表示v的第一个元素,e表示v尾元素的下一个位置

auto b=v.begin(),e=v.end();  //b和e的类型相同

end成员则负责返回指向容器“尾元素的下一个位置”的迭代器,也就是说,该迭代器指示的是容器的一个本不存在的“尾后“元素。这样的迭代器没有什么实际含义,仅是个标记而已,表示我们已经处理完了容器中的所有元素。end成员返回的迭代器常被作为尾后迭代器或简称为尾迭代器。特殊情况下如果容器为空,则begin和end返回的是同一迭代器。

如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器。

迭代器运算符

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

标准容器迭代器的运算符

*iter        返回迭代器iter所指元素的引用

iter->mem     解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem

++iter       令iter指示容器的下一个元素

--iter         令iter指示容器中的上一个元素

iter1==iter2    判断两个迭代器是否相等(不相等),如果两个迭代器指示的是同一元素或者它们是同一容器的尾后迭代器则相等;反正,不相等

iter1!=iter2       

所有的容器都支持上面容器迭代器支持的所有操作,其中有一个例外不符合公共接口特点——forward_list迭代器不支持递减运算符(--)

和指针类似,也能通过解引用迭代器来获取它所指示的元素,执行解引用的迭代器必须合法并确实指示着某个元素。试图解引用一个非法迭代器或者尾后迭代器都是未定义的行为。

将元素从一个元素移动到另外一个元素

迭代器使用递增(++)运算符来从一个元素移动到下一个元素。从逻辑上来说,迭代器的递增和整数的递增类似,整数的递增是在整数值上”加1“,迭代器的递增则是将迭代器”向前移动一个位置“。

因为end返回的迭代器并不实际指示某个元素,所以不能对其进行递增或解引用的操作。

 

迭代器类型

就像不知道string和vector的size_type成员到底是什么类型一样,一般来说我们也不知道迭代器的精确类型。而实际上,那些拥有迭代器的标准库类型使用iterator和const_iterator来表示迭代器的类型:

vector<int>::iterator it;  //it能读写vector<int>的元素

string::iterator it2;     //it2能读写string对象中的字符

 

vector<int>::const_iterator it3;//it3只能读元素,不能写元素

string::const_iterator it4; //it4只能读元素,不能写元素

const_iterator和常量指针差不多,能读取但不能修改它所指的元素值。相反,iterator的对象可读可写。如果vector对象或string对象是一个常量,只能使用const_iterator;如果vector对象或string对象不是常量,那么既可以使用iterator也能使用const_iterator

 

begin和end操作

begin和end返回的具体类型由对象是否是常量决定,如果对象时常量,begin和end返回const_iterator;如果对象不是常量,返回iterator:

vector<int> v;

const vector<int> cv; 

auto it1=v.begin();  //it1的类型是vector<int>::iterator;

auto it2=cv.begin();  //it2的类型是vector<int>::const_iterator

有时候这种默认的行为并非我们想要的。为了便于专门得到const_iterator类型的返回值,C++11新标准引入了两个函数,分别是cbegin和cend:

auto it3=v.cbegin();  //it3的类型是vector<int>::const_iterator

类似于begin和end,上述两个新函数也分别返回指示容器第一个元素或最后元素下一个位置的迭代器。有所不同的是,不论vector对象本身是不是常量,返回值都是const_iterator。

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

虽然vector对象可以动态地增长,但是也有一些副作用。已知的一个限制是不能再范围for循环中向vector对象添加元素。另外一个限制是任何一种可能改变vector对象容量的操作,比如push_back,都会使该vector对象的迭代器失效。

 

迭代器运算

迭代器的递增运算令迭代器每次移动一个元素,所有的标准库容器都支持递增运算的迭代器。类似的,也能用==和!=对任意标准库类型的两个有效迭代器进行比较。

string和vector的迭代器提供了更多额外的运算符,一方面可使得迭代器的每次移动跨过多个元素,另外也支持迭代器进行关系运算符。所有这些运算被称作迭代器运算。

vector和string迭代器支持的运算

iter+n      迭代器加上一个整数值仍得一个迭代器,迭代器指示的新位置与原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器                                           尾元素的下一个位置

iter-n      迭代器减去一个整数值仍得一个迭代器,迭代器指示的新位置与原来相比向后移动了若干个元素,结果迭代器或者指示容器内的一个元素,或者指示容器尾                                元素的下一个位置

iter1+=n    迭代器加法的复合赋值语句,将iter1加n的结果赋给iter1

iter1-=n     迭代器减法的复合赋值语句,将iter1减n的结果赋给iter1

iter1-iter2       两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后将得到左侧的迭代器。参与运算的两个迭代器必须指向的                       是同一个容器中的运算或者尾元素的下一个位置

> >= < <=       迭代器的关系运算符,如果某迭代器指向的容器位置在另一个迭代器所指位置之前,则说明前者小于后者,参与运算的两个迭代器必须指向的是同一个容器中的         元素或者尾元素的下一个位置

上面列出的所有支持的算术运算,这些运算只能应用于string、vector、deque和array的迭代器。我们不能将它们用于其他任何类型的迭代器

迭代器的算术运算

可以令迭代器和一个整数值相加(或相减),其返回值是向前(向后)移动了若干个位置的迭代器。执行这样的操作时,结果迭代器或者指示原vector对象(或string)内的一个元素,或者指示原vector(或string对象)尾元素的下一个位置。

对于string或vector的迭代器来说,除了判断是否相等,还能使用关系运算符(< <= > >=)对其进行比较。参与比较的俩个迭代器必须合法而且指向的是同一个容器的元素(或者尾元素的下一个位置)。

只要两个迭代器指向的同一个容器中的运算或者尾元素的下一位置,就能将其相减,所得结果是两个迭代器的距离。所谓距离指的是右侧的迭代器向前移动多少位置就能追上左侧的迭代器,其类型是名为difference_type的带符号整型数。string和vector都定义了difference_type,因为这个距离可正可负,所以difference_type是带符号类型的。

 

posted @ 2014-08-03 13:24  Jessica程序猿  阅读(1148)  评论(0编辑  收藏  举报