我们已经知道可以使用下标运算符来访问string对象的字符或者vector对象的元素,还有另外一种方法也可以实现同样的目的,那就是迭代器。和指针类似,迭代器提供了对对象的间接访问,其对象是容器中的元素。
1.使用迭代器
和指针不同的是,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员:
auto b = v.begin(), e = v.end();
begin成员负责返回指向第一个元素的迭代器,end成员负责返回指向容器尾元素的下一位置的迭代器,也就是说,该迭代器指示的是容器中一个不存在的尾后元素,称作尾后迭代器。
如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器。
一般来说,我们不用在意迭代器的准确类型是什么,一般使用auto关键字由编译器自动推导出类型。
迭代器运算符
运算符 | 说明 |
---|---|
*iter | 返回iter所指元素的引用 |
iter->mem | 解引用iter并获取该元素名为mem的成员,等价于(*iter).mem |
++iter | 令iter指向容器中的下一个元素 |
–iter | 令iter指向容器中的上一个元素 |
iter1==iter2 | 判断两个迭代器是否相等,如果指向同一个元素或者都是尾后迭代器则相等 |
iter1!=iter2 | 判断两个迭代器是否相等,如果指向同一个元素或者都是尾后迭代器则相等 |
关键概念:泛型编程
原来使用C的程序员在使用C++后,会对for循环中使用!=而非<进行判断感到奇怪。C++习惯使用!=,其原因和他们更愿意使用迭代器而非下标的原因一样:因为这种风格在标准库提供的所有容器上都有效。所有的标准库都定义了!=和==,但是它们中大多数都没有定义<运算符。因此,只要我们养成使用迭代器!=的习惯,就不用太在意到底使用哪种类型的容器。
迭代器类型
拥有迭代器的标准库类型使用iterator和const_iterator来表示迭代器的类型:
vector<int>::iterator it1; //it1能读写vector<int>的元素
string::iterator it2; //it2能读写string对象中的字符
vector<int>::const_iterator it3; //it3只能读元素,不能写元素
string::const_iterator it4; //it4只能读字符,不能写字符
const_iterator和常量指针类似,能读但是不能修改它所指的元素值,相反,iterator的对象可读可写。如果容器的对象是一个常量,只能使用const_iterator,如果不是常量,则都可以使用。
begin和end运算符
begin和end返回的具体类型由对象是否是常量决定,如果是常量,返回const_iterator,如果不是返回iterator。
有时候这种行为并不是我们想要的,如果对象只需要读操作不需要写操作的话最好使用常量类。为了便于专门得到const_iterator类型,C++11新标准引入来两个新函数,分别是cbegin和cend,不论vector对象本身是否是常量,返回值都是const_iterator。
auto iter1 = v.cbegin(), iter2 = v.cend();
2.迭代器运算
下面是迭代器支持的运算:
运算 | 描述 |
---|---|
iter + n | 迭代器加上一个整数仍得一个迭代器,迭代器指示新位置与原来相比向前移动了n个元素 |
iter - n | 迭代器减去一个整数仍得一个迭代器,迭代器指示新位置与原来相比向后移动了n个元素 |
iter += n | 迭代器加法复合赋值语句,将iter1加n的结果赋给iter1 |
iter -= n | 迭代器减法复合赋值语句,将iter1减n的结果赋给iter1 |
iter1 - iter2 | 两个迭代器相减的结果是它们之间的距离 ,两个迭代器必须指向同一个容器中的元素,所得结果的类型是difference_type的带符号的整数 |
<,<=,>,>= | 迭代器关系运算符,参与运算的两个迭代器必须指向同一个容器中的元素或者尾元素的下一位置 |