迭代器运算详解(四十二)
1. 迭代器的随机访问运算
对于 vector 和 string 这样的容器,它们的迭代器支持以下随机访问运算符:
运算符 | 说明 |
---|---|
iter + n | 返回一个新的迭代器,该迭代器比原来的迭代器 iter 向前移动了 n 个位置(即指向后面的第 n 个元素);结果可能指向容器内的某个元素或容器尾后位置。 |
iter - n | 返回一个新的迭代器,该迭代器比原来的迭代器 iter 向后移动了 n 个位置(即指向前面的第 n 个元素);结果同样可能指向一个合法元素或者容器前的“无效位置”。 |
iter1 += n | 使迭代器 iter1 前移 n 个位置,等价于 iter1 = iter1 + n 。 |
iter1 -= n | 使迭代器 iter1 后移 n 个位置,等价于 iter1 = iter1 - n 。 |
iter1 - iter2 | 两个迭代器相减得到它们之间的距离,即 iter1 与 iter2 之间隔了多少个元素。结果为一个带符号的整数,其类型为容器定义的 difference_type 。 |
<, <=, >, >= | 关系运算符:如果两个迭代器指向同一容器中不同位置,则可以根据它们在容器中的顺序来进行比较。例如,如果 iter1 指向的元素在 iter2 之前,则 iter1 < iter2 为真。 |
注意:
参与随机访问运算的两个迭代器必须指向同一容器中的元素或该容器的尾后位置,否则结果未定义。
2. 示例代码
2.1 迭代器算术运算示例
下面的示例代码展示了如何利用迭代器的随机访问运算符来定位 vector 中间的元素,并展示了迭代器的加法、减法以及差值计算。
#include <iostream>
#include <vector>
using std::vector;
using std::cout;
using std::endl;
int main() {
// 定义一个包含20个元素的 vector
vector<int> vi(20);
// 初始化 vi,每个元素赋予其下标值
for (size_t i = 0; i < vi.size(); ++i)
vi[i] = static_cast<int>(i);
// 使用迭代器移动到中间位置
auto mid = vi.begin() + vi.size() / 2;
cout << "The middle element is: " << *mid << endl;
// 演示迭代器减法:计算 mid 与 vi.begin() 之间的距离
auto distance = mid - vi.begin(); // 类型为 vector<int>::difference_type
cout << "Distance from begin() to mid: " << distance << endl;
// 演示关系运算符
auto it = vi.begin();
if (it < mid)
cout << "The first element is before the middle element." << endl;
return 0;
}
运行结果可能为:
The middle element is: 10
Distance from begin() to mid: 10
The first element is before the middle element.
2.2 利用迭代器进行二分搜索
迭代器的随机访问运算使得在有序容器中实现二分搜索非常方便。下面的代码展示了如何在一个有序 vector 中使用迭代器完成二分搜索:
#include <iostream>
#include <vector>
#include <string>
using std::vector;
using std::string;
using std::cout;
using std::endl;
int main() {
// 假设 text 已排序
vector<string> text = {"alpha", "beta", "delta", "epsilon", "gamma"};
string sought = "delta";
// 初始化迭代器,定义搜索范围
auto beg = text.begin(), end = text.end();
// 使用迭代器的随机访问特性进行二分搜索
auto mid = beg + (end - beg) / 2;
while (mid != end && *mid != sought) {
if (sought < *mid) {
end = mid; // 目标在前半部分
} else {
beg = mid + 1; // 目标在后半部分
}
mid = beg + (end - beg) / 2;
}
if (mid != end)
cout << "Found: " << *mid << endl;
else
cout << "Not found." << endl;
return 0;
}
该示例中,迭代器运算符(+
、-
、/
)帮助我们计算新的中间位置,同时使用比较运算符 (<
) 判断迭代器所指位置的先后关系,实现了经典的二分搜索算法。
3. 迭代器与指针的异同
-
相似性:
迭代器类似于指针,可以解引用获取其所指的元素,也支持递增、递减等算术运算。 -
区别:
迭代器是抽象的概念,针对不同容器有不同的具体实现。对于 vector 和 string 这种随机访问容器,迭代器支持完整的随机访问运算;而对于其他容器(如 list),迭代器可能只支持双向移动,而不支持随机访问。 -
difference_type:
两个迭代器相减得到的差值,其类型由容器定义的 difference_type 表示,这通常是带符号整型,因为差值可能为负数。
4. 组合运算与箭头运算符
迭代器还支持箭头运算符 (->
),用于访问迭代器所指对象的成员。等价于先解引用再用点运算符。例如:
#include <iostream>
#include <vector>
#include <string>
using std::vector;
using std::string;
using std::cout;
using std::endl;
struct Person {
string name;
int age;
};
int main() {
vector<Person> people = {{"Alice", 30}, {"Bob", 25}};
auto it = people.begin();
// 使用箭头运算符访问 Person 对象的成员
cout << "Name: " << it->name << ", Age: " << it->age << endl;
return 0;
}
5. 注意事项
-
迭代器失效:
当对容器执行插入、删除或改变容量的操作(如 push_back)时,迭代器可能失效。使用迭代器遍历容器时,不应在循环体内修改容器的大小,否则会导致未定义行为。 -
合法性检查:
使用迭代器进行运算时,应确保参与运算的两个迭代器均指向同一个容器,并且操作结果仍在有效范围内。
6. 总结
-
迭代器是通用访问工具:
可用于遍历容器和 string 对象,并支持随机访问运算(+、-、关系运算等),使得容器操作更灵活。 -
常用迭代器运算:
利用iter + n
、iter - n
可直接定位到容器中的任意位置;通过iter1 - iter2
可计算两个迭代器之间的距离;使用==
、!=
以及<, <=, >, >=
可比较迭代器位置。 -
迭代器与指针相似:
迭代器提供了解引用 ( *iter ) 和箭头运算符 ( iter->mem ) 等操作,但其具体实现依赖于容器类型。 -
迭代器失效需注意:
在遍历过程中不要修改容器的大小,避免迭代器失效引发不可预知的错误。
通过深入理解迭代器的运算及其实际应用,你可以更高效地遍历和操作容器内的元素,同时编写出更健壮的 C++ 程序。
参考资料
- cppreference.com 关于 std::vector、std::string 以及迭代器的详细说明
- 各大 C++ 编码规范(如 Google C++ Style Guide)中对迭代器使用的建议
希望这篇详细讲解能帮助你全面了解迭代器的运算及使用方法,为在实际编程中高效、安全地处理容器数据提供坚实基础。