迭代器介绍
可以使用下标运算符来访问string对象的字符或vector对象的元素,还有另外一种更通用的机制也可以实现同样的目的,这就是迭代器(iterator)。
类似于指针类型,迭代器也提供了对对象的间接访问。就迭代器而言,其对象是容器中的元素或者string对象中的字符。使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另外一个元素。
1. 使用迭代器
和指针不一样的是,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员。比如,这些类型都拥有名为begin和end的成员,其中begin成员负责返回指向第一个元素(或第一个字符)的迭代器。如有下述语句:
// b表示v的第一个元素,e表示v尾元素的下一位置
auto b = v.begin(), e = v.end(); // b和e的类型相同
end成员则负责返回指向容器(或string对象)“尾元素的下一位置”的迭代器,也就是说,该迭代器指示的是容器的一个本不存在的“尾后”元素。这个迭代器没什么实际含义,仅是个标记而已,表示我们已经处理完了容器中的所有元素。end成员返回的迭代器常被称作尾后迭代器。
如果容器为空,则begin和end返回的都是同一个迭代器,都是尾后迭代器。
- 迭代器运算符
和指针类似,也能通过解引用迭代器来获取它所指示的元素,执行解引用的迭代器必须合法并确实指示着某个元素。试图解引用一个非法迭代器或者尾后迭代器都是未被定义的行为。
string s("some string");
if (s.begin() != s.end()) { // 确保s非空
auto it = s.begin(); // it表示s的第一个字符
*it = toupper(*it); // 将当前字符改写成大写形式
}
- 将迭代器从一个元素移动到另外一个元素
迭代器使用递增(++)运算符来从一个元素移动到下一个元素。
因为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只能读字符,不能写字符
- begin和end运算符
begin和end返回的具体类型由对象是否是常量决定,如果对象是常量,begin和end返回const_iterator;如果对象不是常量,返回iterator
为了便于专门得到const_iterator类型的返回值,C++11新标准引入了两个新函数,分别是cbegin和cend.不论vector对象本身是否是常量,返回值都是const_iterator。
- 结合解引用和成员访问操作
解引用迭代器可获得迭代器所指的对象,如果该对象的类型恰好是类,就有可能希望进一步访问它的成员。
例如,对于一个由字符串组成的vector对象来说,要想检查其元素是否为空,令it是该vector对象的迭代器,只需检查it所指字符串是否为空就可以了,其代码如下,
(*it).empty()
为了简化上述表达式,C++定义了箭头运算符(->)。箭头运算符把解引用和成员访问两个操作结合在一起,也就是说,it->mem
和(*item).mem
表达的意思相同
- 某些对vector对象的操作会使迭代器失效
虽然vector对象可以动态地增长,但是也会有一些副作用。已知的一个限制是不能在范围for循环中向vector对象添加元素。另外一个限制是任何一种可能改变vector对象容量的操作,比如push_back,都会使该vector对象的迭代器失效。
谨记,但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。
2. 迭代器运算
string和vector迭代器提供了更多额外的运算符,一方面可使得迭代器每次移动跨过多个元素,另外也支持迭代器进行关系运算。所有这些运算被称作迭代器运算
所谓两个迭代器之间的距离指的是右侧的迭代器向前移动多少位置就能追上左侧的迭代器,其类型是名为difference_type的带符号整型数。
评论 (0)