与下标运算符实现相同的目的,就是迭代器(iterator)。
有些容器类型不支持下标但支持迭代器
迭代器和指针类似,提供对对象的间接访问。
就迭代器而言,对象是容器中的元素或者string对象中的字符
3.4.1使用迭代器
获取迭代器不适用取地址符
有迭代器的类型同时返回拥有返回迭代器的成员
这些类型拥有名为 begin 和 end的成员
//由编译器决定b和e的类型
//b表示v的第一个元素,e表示v尾元素的下一位置
auto b = v.begin(), e = v.end();// b和e的类型相同
该迭代器表示一个本不存在的 尾后(off the end)元素
没有实际意义,仅是标记
end返回的迭代器称为end iterator或off-the-end iterator
Note: 如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器。
不在意迭代器准确返回的类型
迭代器运算符
*iter | 返回迭代器iter所指元素的引用 |
iter->men | 解引用iter并获取该元素的名为men的成员,等价于(*iter).men |
++iter | 令iter指示容器中的下一个元素 |
--iter | 令iter指示容器中的上一个元素 |
iter1 == iter2 != | 判断两个迭代器是否相等(不相等),如果两个迭代器指示的是同一个元素或者他们是同一个容器的尾后迭代器,则相等;反之不等 |
举个例子
string s("some string");
if (s.begin() != s.end()){
auto it = s.begin();
*it = toupper(*it);
}
将迭代器从一个元素移动到另外一个元素
Note:因为end返回的迭代器并不实际指示某个元素,所以不能对其进行递增或解引用的操作
例:
for(auto it = s.begin();it/*(这里it没有 *迭代器的判断)*/ !=s.end() && ! isspace(*it);++it)
*it = toupper(*it);
关键概念:泛型编程
养成使用迭代器和!= 的习惯
迭代器类型
就像不知道string和vector的size_type 成员是什么类型,也不知道迭代器的精准类型
拥有迭代器的标准库类型使用 iterator和const_iterator(不用初始化?)来表示迭代器的类型
vector<int>::iterator it; //it能读写vector<int>的元素
string::iterator it2; //it2能读写iterator中的字符
vector<int>::const_interator it3; //it3只能读不能写
string::const_interator it4; //it4只能读不能写
const_iterator 和常量指针差不多
如果对象是一个常量,只能使用const_iterator
术语:迭代器和迭代类型
迭代器有三种含义:1.可能是迭代器概念本身,2.☞容器定义的迭代器类型 3. 指某个迭代器对象。
我们认为某个类型是迭代器当且仅当它支持一套操作,这套操作使得我们能访问容器的元素后者从某个元素移动到另一个元素
每个容器类定义了一个名为iterator的类型,该类型支持迭代器概念所规定的一套操作
begin和end运算符
begin和end返回的具体类型由是否是常量决定,如果对象是常量,begin和end返回const_iterator;如果对象不是常量,返回 iterator
vector<int> v;
const vector<int> cv;
auto it1 = v.begin(); //it1的类型是 vector<int>::iterator
auto it2 = cv.begin(); //it2的类型是 vector<int>::const_iterator
如果对象只需要读操作而无需写操作的话最好用常量类型
c++11引入了 cend 和cbegin
auto = v.cbegin();// it3的类型是vector<int>::const_iterator
结合解引用和成员访问操作
检查元素是否为空
令 it是vector对象的迭代器
(*It).empty()//解引用 it,然后调用结果对象的empty成员
*it.empty() //it试图访问名为empty成员,但it是个迭代器没有empty成员
圆括号必不可少
C++语言定义了 箭头有运算符(->)。箭头运算符把解引用和成员访问两个操作结合在一起
it -> mem 和 (*it).men表达相同的意思(不是用在元素上的,应该是用在成员函数上的?)
假设名为text的字符串向量存放文本中信息
//依次输出text的每一行直至遇到第一个空白行为止
for(auto it = text.cbegin();
it != text.cend() && ! It->empty();++it)
cout << *it <<endl;
某些对vector对象的操作会使迭代器失效
限制不能在范围for循环中向vector对象添加元素。
一种可能改变vector对象容量的操作,会是该vector对象的迭代器失效
warning:用了迭代器的循环体,都不要向迭代器所属的容器添加元素
3.4.2 迭代器运算
vector和string迭代器支持的运算
iter + n | 迭代器家伙是那个一个整数值扔得一个迭代器,迭代器指示的新位置与原来相比向前移动n个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置 |
iter -n | 向后 |
iter+=n | 迭代器加法的赋值语句,将iter1加n的结果赋给iter1 |
Iter-=n | 减法 |
Iter1-iter2 | 两个迭代器相减的结果是他们的距离。参与运算的迭代器必须指向的是同一个容器中的元素或尾元素的下一位置 |
>,>=,<,<= | 指向位置如果一个在另一个之前,前者小于后者... |
迭代器的算数运算
//计算得到vi中间元素的一个迭代器
auto mid = vi.begin() + (vi.size()/2);
两者距离是 difference_type 的带符号整形数
使用迭代器运算
二分搜索
//text必须是有序的
//beg和end表示需要搜索的范围
auto beg = text.begin(), end = text.end();
auto mid = text.begin() + (end-beg)/2;
//当元素尚未检查并且我们还没有找到sought时执行语句
while (mid != text.begin() && *mid != sought){
if(sought < *mid)这不两string比较吗?
end = mid;
else
beg = mid+1;
mid = beg + (end-beg)/2;
}