介绍迭代器
尽管我们可以使用下标来访问字符串中的字符或vector的元素,但更一般的机制是使用迭代器(iterator)。
所有的容器都支持迭代器,但仅少数几个支持下标操作。
合法的迭代器:
- 指示某个元素
- 指示最后一个元素的下一个位置
其它的迭代器都是不合法的。
使用迭代器
使用begin
和end
成员函数。
// b 指示第一个元素;e 指示最后一个元素的下一个位置
auto b = v.begin(), e = v.end();
一般我们不必关心迭代器的准确类型,所以直接使用auto
。
end
返回的迭代器一般被称为off-the-end迭代器
,或者缩写为end迭代器
。
显然,如果一个容器为空,begin
返回的和end
返回的相同。
迭代器的操作
| 方法 | 解释 | | iter | 返回指示元素的引用 | | iter->mem | 解引用iter,并获取名字为mem的成员,等价于 (iter).mem | | ++iter | 增加iter,指示下一个 | | --iter | 减小iter,指示前一个 | | == , != | 比较 |
下面是把遇到空白字符前的字符转成大写。
for(auto it = s.begin(); it != s.end() && !isspace(*it); ++it)
*it = toupper(*it);
熟悉C或者Java语言的人可能需要习惯C++里面for循环一般都是使用!=结束,而不是使用<。 这是因为,所有的容器的迭代器都定义了!=和==方法;而绝大部份迭代器没有<方法。通过使用!=,我们可以不必 关心处理容器的准确类型。
迭代器的类型
就像我们不知道vector或string的size_type
的准确类型,我们一般也不知道迭代器的准确类型。
库类型的迭代器定义了iterator
和const_iterator
两种类型。
vector<int>::iterator it; // 可读,可写
vector<int>::iterator it2; // 可读,可写
vector<int>::const_iterator it3; // 可读,不能写
const_iterator
的行为类似一个const指针。就像const指针,const_iterator
不能修改所指示的元素。如果
一个vector或者字符串是const的,那么只能使用const_iterator
。
如果对象是const的,那么begin
和end
返回的就是const_iterator
;如果对象不是const的,返回的就是iterator。
但这种行为有时不是我们想要的,即针对非const对象,我们也希望得到const_iterator
。C++ 11引入了两个新的函数,
cbegin
和cend
解决了这一问题。
auto it3 = v.cbegin();
解引用和访问成员
当对迭代器解引用时,得到的是其指示的对象。如果该对象是个类类型的,我们可能要访问其的成员。举个例子,一个字符串的vector可能想知道
给定元素是否为空,可以使用(*it).empty()
。
需要注意的是,(*it).empty()
这个括号是必须的。否则,点操作符直接作用于it。因此,*it.empty()
是错误的。
为了简化这种表示,语言定义了箭头操作符(->),它把解引用和成员访问组合为一个符号,即it->empty()
。
迭代器的算术
自增与自减是所有迭代器都支持的操作。
而对于string和vector的迭代器,还支持额外的算术操作。
| 方法 | | iter + n | | iter - n | | iter1 += n | | iter2 -= n | | iter1 - iter2 | | >, >=, <, <= |
比如,计算vector中间位置,
auto mid = vi.begin() + vi.size() / 2;
需要注意的是,迭代器的相加是不合法的。