介绍
通过使用迭代器(iterator),可以访问string对象中的字符或vector对象中的元素。在第II部分中将要介绍,除了vector之外,标准库还定义了其他几种容器。所有标准库容器都可以使用迭代器,但是其中只有少数几种才同时支持下标运算符。另外,严格来说,string对象不属于容器类型,但是string支持很多与容器类型类似的操作,如string也支持迭代器。
使用迭代器
拥有迭代器的类型同时会拥有返回迭代器的成员函数。比如,这些类型都拥有名为begin和end的成员。其中begin成员负责返回指向第一个元素(或第一个字符)的迭代器,如下:
// b表示v的第一个元素,e表示v尾元素的下一位置
auto b = v.begin(), e = v.end(); // b和e类型相同
end成员负责返回指向容器(或string对象)“尾元素的下一位置(one past the end)”的迭代器。这样的迭代器没什么实际含义,仅是个标记而已,表示我们已经处理完了容器的所有元素。end成员返回的迭代器常被称作尾后迭代器(end iterator)。特殊情况下如果容器为空,则begin和end返回的是同一个迭代器。
一般来说,我们不清楚(不在意)迭代器准确的类型到底是什么。之后将对相关内容做更详细介绍。
迭代器运算符
迭代器用递增(++)运算符来从一个元素移动到下一个元素,递减运算符(–)同理。之前有一个程序把string对象中第一个单词改写为大写形式,现在利用迭代器及递增运算符可以实现相同的功能:
// 依次处理s的字符直至我们处理完全部字符或遇到空表
for(auto it = s.begin(); it != s.end() && !isspace(*it); ++it)
*it = toupper(*it); // 将当前字符改成大写形式
C++程序员习惯性地使用 !=,其原因和他们更愿意使用迭代器而非下标的原因一样:因为这种编程风格在标准库提供的所有容器上都有效。
之前已经说过,只有string和vector等一些标准库类型有下标运算符,而并非全都如此。与之类似,所有标准库容器的迭代器都定义了 == 和 !=,但是它们中的大多数都没有定义 < 运算符。因此,只要我们养成使用迭代器和 != 的习惯,就不用太在意用的到底是哪种容器类型。
迭代器类型
一般来说或我们也不知道(其实是无须知道)迭代器的精确类型。实际上,那些拥有迭代器类型的标准库类型使用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返回的具体类型由对象是否是常量决定,如果对象是常量,它们返回const_iterator,如果不是常量,它们返回iterator。
有时这种默认的行为并非我们所要。在6.2.3节(p191)将会看到,如果对象只需读操作而无需写操作的话最好使用常量类型(比如const_iterator)。C++11新标准引入了两个新函数,分别是cbegin和cend,不论vector对象(或string对象)本身是否是常量,它俩的返回值都是const_iterator:
auto it3 = v.cbegin(); // it3 的类型是 vector<int>::const_iterator
结合解引用和成员访问操作
C++语言定义了**箭头运算符(->)**把解引用和对元素里的成员访问两个操作结合在了一起,即 it->num 和 (*it).num表达的意思是相同的。
例如,用一个名为text的字符串vector存放文本文件中的数据,其中的元素只可能是一句话或者是一个用于表示段落分隔符的空字符串,如果要输出text中第一段的内容,可以利用迭代器写一个循环令其便利text,直到遇到空字符串的元素为止:
for(auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it)
cout << *it << endl;
某些对vector对象的操作会使迭代器失效
虽然vector对象可以动态地增长,但是也会有一些副作用。我们已知的一个限制是,不能在范围for循环中向vector对象添加元素。另外一个限制是,任何一种可能改变vector对象容量的操作,比如push_back,都会使该vector对象的迭代器失效。
谨记,但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。
迭代器运算
对于string或vector的迭代器来说,除了判断是否相等,还能使用关系运算符(<、<=、>、>=)对其进行比较。参与比较的两个迭代器必须合法而且指向的是同一个容器的元素(或者尾元素的下一位置)。例如,假如it和mid是同一个vector对象的两个迭代器,可以用下面的代码来比较他们所指的位置孰前孰后:
if(it < mid)
// 处理 vi 前半部分的元素
只要两个迭代器指向的是同一个容器中的元素或者尾元素的下一位置,就能将其相减,所的结果是两个迭代器的距离。所谓距离指的是右侧的迭代器向前移动多少位置能追上左侧的迭代器,其类型是名为difference_type的带符号整数。string和vector都定义了difference_type,因为这个距离可正可负,所以是带符号类型的。