C++Primer第3章(4)迭代器介绍

3.4 迭代器介绍

​ 迭代器提供了对对象的间接访问.就迭代器而言,其对象时容器中的元素或者string对象中的字符.使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另外一个元素.迭代器有有效和无效之分,有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置:其他所有情况都属于无效

3.4.1 使用迭代器

​ 有迭代器的类型同时拥有返回迭代器的成员.比如,这些类型都有名为beginend的成员,其中begin成员负责返回指向第一个元素(或第一个字符的迭代器).

//b表示v的第一个元素,e表示v尾元素的下一位置
auto b = v.begin(), e = v.end();//b和e的类型相同

​ end成员负责返回指向容器(或string对象)"尾元素的下一位置"的迭代器,该迭代器指示的是容器的一个本不存在的"尾后"元素.这种迭代器没什么实际含义,就是个标记而已,表示我们已经处理完了容器中的所有元素.

​ end成员返回的迭代器常被称作尾后迭代器或者简称尾迭代器.特殊情况下如果容器为空,则begin和end返回的是同一个迭代器
image-20211130214512081

迭代器运算符

image-20211130220333495

可以通过解引用迭代器来获取它所指示的元素,执行解引用的迭代器必须合法并确实指示着某个元素. 试图解引用一个非法迭代器或者尾后迭代器都是未被定义的行为

image-20211202093956047

将迭代器从一个元素移动到另外一个元素

迭代器使用递增(++)运算符来从一个元素移动到下一个元素. 从逻辑上来说,迭代器的递增和整数的递增类似,整数的递增是在整数值上"加1",迭代器的递增则是将迭代器"向前移动一个位置".

因为end返回的迭代器并不实际指向某个元素,所以不能对其进行递增或解引用的操作

image-20211202094755254

​ 每次迭代的最后,执行++it令迭代器前移一个位置以访问s的下一个字符.

image-20211202094858529

迭代器类型

​ 一般来说我们也不知道(其实是无须知道)迭代器的精确类型.而实际上,那些拥有迭代器的标准库类型使用iteratorconst_iterator来表示迭代器的类型:

vector<int>::iterator it; //it能读写vector<int>的元素
string::iterator it2;   //it2能读写string对象中的字符

vector<int>::const_iterator it3; //it3只能读元素,不能写元素
string::const_iterator it4; //it4只能读字符,不能写字符

​ const_iterator和常量指针差不多,能读取但不能修改它所指的元素值。相反,iterator的对象可读可写.如果vector对象或string对象是一个常量,只能使用const_iterator;如果vector对象或string对象不是常量,那么既能使用iterator也能使用const_iterator.

image-20211202125726555

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

​ 如果对象只需读操作而无须写操作的话最好使用常量类型. 为了便于专门得到 const_iterator 类型的返回值, C++11新标准引入了两个新函数, 分别是 cbegincend.

auto it3 = v.cbegin();//it3的类型是vector<int>::const_iterator

​ cbegin 和 cend 不论 vector对象(或string对象)本身是否是常量, 返回值都是const_iterator.

结合解引用和成员访问操作

​ 解引用迭代器可获得迭代器所指的对象, 如果该对象的类型恰好是类, 就有可能希望进一步访问它的成员.

​ 对于一个由字符串组成的vector对象来说, 要想检查其元素是否为空, 令it是该vector对象的迭代器, 只需检查it所指字符串是否为空就可以

(*it).empty();

(*it).empty()中的圆括号必不可少, 该表达式的含义是先对it解引用, 然后解引用的结果再执行点运算符. 如果不加圆括号, 点运算符将由it来执行, 而非it解引用的结果

(*it).empty()   //解引用it,然后调用结果对象的empty成员
*it.empty()   //错误,试图访问it的名为empty的成员,但it是个迭代器,没有empty成员

​ 为了简化上述表达式, C++语言定义了箭头运算符(->). 该运算符把解引用和成员访问两个操作结合在一起, 也就是说, it->mem和(*it).mem表达的意思相同.

某些对vector对象的操作会使迭代器失效

  • 不能在范围for循环中向vector对象添加元素
  • 任何一种可能改变vector对象容量的操作, 比如push_back, 都会使该vector对象的迭代器失效.

但凡是使用了迭代器的循环体, 都不要向迭代器所属的容器添加元素.

3.4.2 迭代器运算

image-20211202132355828

迭代器的算术运算

​ 可以令迭代器和一个整数值相加(或相减),其返回值是向前(或向后)移动了若干个位置的迭代器。执行这样的操作时,结果迭代器或者指示原vector对象(或string对象)内的一个元素,或者指示原vector对象(或string对象)尾元素的下一位置。

auto mid = vi.begin() + vi.size() / 2; //计算得到最接近vi中间元素的一个迭代器

​ 对于string或vector的迭代器来说, 除了判断是否相等, 还能使用关系运算符(<, <=, >, >=)对其进行比较. 参与比较的两个迭代器必须合法而且指向的是同一个容器的元素(或者尾元素的下一位置). 例如, 假设it和mid是同一个vector对象的两个迭代器, 可以用下面的代码来比较它们所指的位置孰前孰后

if(it < mid)
	// 处理vi前半部分的元素

​ 只要两个迭代器指向的是同一个容器中的元素或者尾元素的下一位置,就能将其相减,所得结果是两个迭代器的距离. 所谓距离指的是右侧的迭代器向前移动多少位置就能追上左侧的迭代器,其类型是名为difference_type 的带符号整型数. string 和vector都定义了difference_type ,因为这个距离可正可负,所以difference_ type是带符号类型的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

远离蒙昧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值