迭代器支持的运算

迭代器为所有标准库容器类型所提供的运算

*iter

返回迭代器 iter 所指向的元素的引用

iter->mem

对 iter 进行解引用,获取指定元素中名为 mem 的成员。等效于(*iter).mem

++iter
iter++

给 iter 加 1,使其指向容器里的下一个元素

--iter
iter--

给 iter 减 1,使其指向容器里的前一个元素

iter1 ==iter2
iter1 !=iter2

比较两个迭代器是否相等(或不等)。当两个迭代器指向同一个容器中的同一个元素,或者当它们都指向同一个容器的超出末端的下一位置时,两个迭代器相等

vector 和 deque 容器的迭代器提供额外的运算

iter + n
iter - n

在迭代器上加(减)整数值 n,将产生指向容器中前面(后面)第 n个元素的迭代器。新计算出来的迭代器必须指向容器中的元素或超出容器末端的下一位置

iter1 +=iter2
iter1 -=iter2

这里迭代器加减法的复合赋值运算:将 iter1 加上或减去 iter2 的运算结果赋给 iter1

iter1 -iter2

两个迭代器的减法,其运算结果加上右边的迭代器即得左边的迭代器。这两个迭代器必须指向同一个容器中的元素或超出容器末端的下一位置只适用于 vector 和 deque 容器

iter + n
iter - n

在迭代器上加(减)整数值 n,将产生指向容器中前面(后面)第 n个元素的迭代器。新计算出来的迭代器必须指向容器中的元素或超出容器末端的下一位置

>, >=,<, <=

迭代器的关系操作符。当一个迭代器指向的元素在容器中位于另一个迭代器指向的元素之前,则前一个迭代器小于后一个迭代器。关系操作符的两个迭代器必须指向同一个容器中的元素或超出容器末端的下一位置只适用于 vector 和 deque 容器

关系操作符只适用于 vector 和 deque 容器,这是因为只有这种两种容器为其元素提供快速、随机的访问。它们确保可根据元素位置直接有效地访问指定的容器元素。这两种容器都支持通过元素位置实现的随机访问,因此它们的迭代器可以有效地实现算术和关系运算。

例如,下面的语句用于计算 vector 对象的中点位置:

vector<int>::iterator iter = vec.begin() + vec.size()/2;

另一方面,代码:

// copy elements from vec into ilist
list<int> ilist(vec.begin(), vec.end());
ilist.begin() + ilist.size()/2; // error: no addition on list iterators

是错误的。list 容器的迭代器既不支持算术运算(加法或减法),也不支持关系运算(<=, <, >=, >),它只提供前置和后置的自增、自减运算以及相等(不等)运算。

迭代器范围

C++ 语言使用一对迭代器标记迭代器范围(iterator range) ,这两个迭代器分别指向同一个容器中的两个元素或超出末端的下一位置, 通常将它们命名为first 和 last,或 beg 和 end,用于标记容器中的一段元素范围。尽管 last 和 end 这两个名字很常见,但是它们却容易引起误解。其实第二个迭代器从来都不是指向元素范围的最后一个元素, 而是指向最后一个元素的下一位置。该范围内的元素包括迭代器 first 指向的元素,以及从 first 开始一直到迭代器 last 指向的位置之前的所有元素。此类元素范围称为左闭合区间(left-inclusive interval) ,其标准表示方式为:

// to be read as: includes first and each element up to but not including last
[ first, last )

迭代器 last 可以等于 first,或者指向 first 标记的元素后面的某个元素,但绝对不能指向first 标记的元素前面的元素。

对形成迭代器范围的迭代器的要求:

迭代器 first 和 last 如果满足以下条件,则可形成一个迭代器范围:

它们指向同一个容器中的元素或超出末端的下一位置。

如果这两个迭代器不相等, 则对 first 反复做自增运算必须能够到达 last。换句话说,在容器中,last 绝对不能位于 first 之前。

使用左闭合区间的编程意义

假设first 和 last 标记了一个有效的迭代器范围,于是:

当 first 与 last 相等时,迭代器范围为空;当 first 与不相等时,迭代器范围内至少有一个元素,而且 first 指向该区间中的第一元素。此外,通过若干次自增运算可以使 first 的值不断增大,直到 first == last 为止。

这两个性质意味着程序员可以安全地编写如下的循环,通过测试迭代器处理一段元素:

while (first != last) {
// safe to use *first because we know there is at least one
element
++first;
}

假设 first 和 last 标记了一段有效的迭代器范围,于是我们知道要么first == last,这是退出循环的情况;要么该区间非空,first 指向其第一个元素。因为 while 循环条件处理了空区间情况,所以对此无须再特别处理。当迭代器范围非空时,循环至少执行一次。由于循环体每次循环就给 first 加 1,因此循环必定会终止。而且在循环内可确保 *first 是安全的:它必然指向first 和 last 之间非空区间内的某个特定元素。

使迭代器失效的容器操作

一些容器操作会修改容器的内在状态或移动容器内的元素。这样的操作使所有指向被移动的元素的迭代器失效,也可能同时使其他迭代器失效。使用无效迭代器是没有定义的,可能会导致与悬垂指针相同的问题。

例如,每种容器都定义了一个或多个 erase 函数。这些函数提供了删除容器元素的功能。任何指向已删除元素的迭代器都具有无效值,毕竟,该迭代器指向了容器中不再存在的元素。

使用迭代器编写程序时,必须留意哪些操作会使迭代器失效。使用无效迭代器将会导致严重的运行时错误。

无法检查迭代器是否有效,也无法通过测试来发现迭代器是否已经失效。任何无效迭代器的使用都可能导致运行时错误,但程序不一定会崩溃。