迭代器介绍与使用(四十一)

1. 迭代器的基本概念

  • 迭代器是什么?

    迭代器可以看作是一种特殊的指针,用于访问容器中的元素。一个迭代器要么指向容器中的某个有效元素,要么指向"尾后位置"(one past the end),即容器最后一个元素之后的位置。尾后迭代器本身没有实际意义,仅用作循环终止的标记。

  • 获取迭代器

    大多数标准容器都提供两个成员函数:begin()end()

    • begin() 返回指向容器中第一个元素的迭代器。
    • end() 返回一个尾后迭代器,即指向容器最后一个元素之后的位置。
      如果容器为空,则这两个函数返回相同的迭代器。

例如,针对一个 vector:

cpp 复制代码
#include <vector>
#include <iostream>
using std::vector;
using std::cout;
using std::endl;

int main() {
    vector<int> v = {1, 2, 3, 4, 5};
    auto it_begin = v.begin(); // it_begin 指向第一个元素
    auto it_end   = v.end();   // it_end 是尾后迭代器
    // 输出第一个元素
    if (it_begin != it_end)
        cout << "First element: " << *it_begin << endl;
    return 0;
}

2. 迭代器的常用操作

标准库容器的迭代器通常支持以下几种基本运算符:

运算符 说明
*iter 解引用,返回迭代器 iter 所指元素的引用。
iter->mem 相当于 (*iter).mem,先解引用 iter,然后访问该元素的成员 mem。
++iter 令 iter 指向容器中的下一个元素。
--iter 令 iter 指向容器中的上一个元素。
iter1 == iter2 判断两个迭代器是否相等(例如,是否指向同一元素或同一容器的尾后位置)。
iter1 != iter2 判断两个迭代器是否不相等。

注意:

尝试解引用一个无效迭代器(例如尾后迭代器或未经初始化的迭代器)会导致未定义行为。

3. 迭代器的使用示例

3.1 访问和修改元素

假设我们有一个 string 对象,我们可以利用迭代器来修改其中的字符。以下示例将 string 中的第一个字符转换为大写:

cpp 复制代码
#include <iostream>
#include <string>
#include <cctype> // 包含 toupper
using std::string;
using std::cout;
using std::endl;

int main() {
    string s("some string");
    // 检查字符串是否为空(begin() 和 end() 相等时为空)
    if (s.begin() != s.end()) {
        auto it = s.begin();   // it 指向第一个字符
        *it = toupper(*it);      // 修改第一个字符为大写
    }
    cout << s << endl;  // 输出: "Some string"
    return 0;
}

3.2 使用迭代器遍历容器

利用范围 for 循环时,也可以直接用迭代器,但有时我们需要手动控制迭代器的移动。例如,下面的代码将 string 中第一个单词转换为大写(遍历直到遇到空白字符):

cpp 复制代码
#include <iostream>
#include <string>
#include <cctype>
using std::string;
using std::cout;
using std::endl;

int main() {
    string s("some string");
    // 使用迭代器遍历字符串中的字符,直到遇到空白字符
    for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it) {
        *it = toupper(*it);
    }
    cout << s << endl; // 输出: "SOME string"
    return 0;
}

3.3 结合箭头运算符访问成员

对于存放类对象的容器,迭代器可以用于访问对象的成员。箭头运算符 -> 可将解引用和成员访问合并为一,例如:

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
using std::vector;
using std::string;
using std::cout;
using std::endl;

int main() {
    vector<string> text = {"Line one", "Line two", ""};
    // 使用 const_iterator 遍历,直到遇到空字符串(段落分隔符)
    for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it) {
        cout << *it << endl;
    }
    return 0;
}

这里,it->empty() 等价于 (*it).empty(),用于检查迭代器所指字符串是否为空。

4. 迭代器类型

每个标准库容器都定义了两种迭代器类型:

  • iterator:可读写,允许修改容器中元素。
  • const_iterator:只读,保证不能通过迭代器修改元素。

例如,对于 vector:

cpp 复制代码
vector<int> v = {1, 2, 3};
vector<int>::iterator it1 = v.begin();       // 可读写迭代器
vector<int>::const_iterator it2 = v.begin();   // 只读迭代器

如果 vector 对象本身是常量,则只能使用 const_iterator:

cpp 复制代码
const vector<int> cv = {1, 2, 3};
auto cit = cv.begin(); // 类型为 vector<int>::const_iterator

另外,C++11 还提供了 cbegin()cend(),无论容器是否为常量,这两个函数都返回 const_iterator,适用于只需读操作的场景。

5. 迭代器失效

需要特别注意的是,当对 vector 进行插入、删除或其他可能改变其容量的操作时,原有迭代器可能会失效。失效的迭代器不再指向正确的元素,任何对其的访问都会导致未定义行为。因此,在使用迭代器的循环体中,不要修改容器的大小。

例如,下面的代码在使用范围 for 遍历时尝试调用 push_back,就会导致迭代器失效:

cpp 复制代码
#include <iostream>
#include <vector>
using std::vector;
using std::cout;
using std::endl;

int main() {
    vector<int> v = {1, 2, 3};
    // 错误示例:在范围 for 循环中修改容器大小
    for (auto &x : v) {
        cout << x << " ";
        v.push_back(x);  // 修改容器,可能使迭代器失效
    }
    cout << endl;
    return 0;
}

在编写涉及迭代器的循环时,必须确保循环过程中不改变容器的大小。

6. 总结

  • 迭代器提供统一访问方式

    迭代器允许遍历所有标准容器和类似容器(例如 string)的元素,是一种比下标运算符更通用的访问方式。

  • 获取迭代器

    通过容器的 begin()end() 成员函数获取迭代器,begin() 指向第一个元素,end() 指向尾后位置。对于只读操作,建议使用 const_iterator 或 cbegin/cend。

  • 迭代器运算

    使用 *iter 解引用迭代器以获取元素,++iter 令迭代器前进,iter->mem 可访问迭代器所指对象的成员。

  • 遍历与修改

    使用范围 for 语句可以简洁地遍历容器,但若需修改元素,必须使用引用形式(如 for(auto &c : container))。

  • 迭代器失效问题

    在循环中不应修改容器大小,因为可能导致迭代器失效,从而引起未定义行为。

通过深入理解迭代器的基本概念与操作,你可以在各种容器中灵活、安全地访问和操作元素。迭代器不仅与指针类似,而且为泛型编程提供了统一接口,是现代 C++ 编程中不可或缺的工具。

参考资料

  • cppreference.com 关于 std::vector 和 std::string 的迭代器说明
  • 各大 C++ 编码规范(如 Google C++ Style Guide)中对迭代器使用的建议

希望这篇博客能帮助你全面了解迭代器的概念和使用方法,从而在实际编程中高效、正确地访问和操作容器内的数据。

相关推荐
末央&7 分钟前
【C++】vector的底层封装和实现
android·c++
码界筑梦坊15 分钟前
基于Python的招聘推荐数据可视化分析系统
开发语言·爬虫·python·信息可视化·数据分析
CptainLee18 分钟前
python小整数池和字符串贮存
java·开发语言·python
nlog3n33 分钟前
Java访问者模式详解
java·开发语言·访问者模式
牛马大师兄38 分钟前
Shell脚本编程之正则表达式
linux·运维·服务器·开发语言·ssh·bash·shell
froginwe1139 分钟前
正则表达式 - 简介
开发语言
似水এ᭄往昔1 小时前
【C语言】编译和链接
c语言·开发语言
慕芊妤1 小时前
Logo语言的数据可视化
开发语言·后端·golang
十七算法实验室1 小时前
Matlab实现鼠群优化算法优化随机森林算法模型 (ROS-RF)(附源码)
开发语言·算法·决策树·随机森林·机器学习·支持向量机·matlab
烁3471 小时前
每日一题(小白)字符串娱乐篇16
java·开发语言·算法·娱乐·暴力