事实上,访问对象既可以使用下标,还可以使用一种更通用的方式——迭代器。除了vector容器,标准库中还有几种其他容器,共同点是都可以使用迭代器,但其中只有少数几种才支持使用下标运算符。
严格来说string不属于容器类型,但string支持类似容器类型的操作。例如和vector容器一样支持下标运算符,也支持迭代器。
迭代器:
类似于指针,迭代器会提供对于对象的间接访问。对于迭代器来说,它的对象可能是容器中的元素或者string中的字符。使用迭代器可以访问元素,也可以移动元素。有效的迭代器可以指向某个元素或者指向容器中尾元素的下一个位置。
迭代器的使用:
与指针不同的是,获取迭代器不使用取地址符&。如下:
auto b = v.begin(), e = v.end(); //b和e的类型相同
// b表示v的第一个元素,e表示v尾元素的下一个位置
end负责返回指向容器尾元素的下一个位置,这个迭代器明显指示一个不存在的元素。没什么实际意义,仅仅作为标记,表示我们处理完了容器中的所有元素。end成员返回的迭代器常被称为尾迭代器。在特殊情况下,如果容器为空,begin和end返回的是同一个迭代器。
标准容器中迭代器常见运算符含义:
*iter :返回迭代器iter所指元素引用
iter->mem :等价于(*iter).mem, 解引用iter并获取该元素的名为mem的成员
++iter :令iter指示容器中的下一个元素
--iter :令iter指示容器中的上一个元素
iter1 == iter2 :判断两个迭代器是否相等,如果两个迭代器指示同一个元素或是同一个容器的同一迭代器则相等
iter1 != iter2
但与指针类似的是,用户可以通过解引用迭代器来获取指示的元素,执行解引用的迭代器必须是合法的,或是说必须被定义过的。
我们可以利用下标运算符把string对象的第一个字母改为大写:
string s("some string");
if (s.begin() != s.end()) { // 确保s非空
auto it = s.begin(); // it表示s的第一个字符
*it = toupper(*it); // 将当前字符改成大写形式
}
现在利用迭代器实现相同功能:
for (auto it = s.begin(); it!=s.end() && !isspace(*it); ++it)
*it = toupper(*it); // 将当前字符改成大写形式
迭代器类型:
iterator和const_iterator可以用来表示迭代器类型:
vector<int>::iterator it;
vector<int>::const_iterator it;
iterator表示可读可写,而const_iterator表示只读不写。
如果vector或string对象是一个常量,那么只能使用const_iterator来表示其为常量。如果不是常量,我们既可以使用iterator也可以使用const_iterator.