现代 C++ 编程实战
吴咏炜
前 Intel 资深软件架构师
34196 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
加餐 (1讲)
现代 C++ 编程实战
15
15
1.0x
00:00/00:00
登录|注册

07 | 迭代器和好用的新for循环

迭代器之间的比较
成员函数和数据成员
类型要求
istream_line_reader类
iterator类
定义输入行迭代器
基于范围的for循环
ostream_iterator
back_inserter_iterator
容器的iterator类型
输出迭代器
连续迭代器
随机访问迭代器
双向迭代器
前向迭代器
输入迭代器
迭代器要求
遍历概念
参考资料
课后思考
内容小结
使用输入行迭代器
常用迭代器
什么是迭代器?
迭代器和新for循环

该思维导图由 AI 生成,仅供参考

你好,我是吴咏炜。
我们已经讲过了容器。在使用容器的过程中,你也应该对迭代器(iterator)或多或少有了些了解。今天,我们就来系统地讲一下迭代器。

什么是迭代器?

迭代器是一个很通用的概念,并不是一个特定的类型。它实际上是一组对类型的要求([1])。它的最基本要求就是从一个端点出发,下一步、下一步地到达另一个端点。按照一般的中文习惯,也许“遍历”是比“迭代”更好的用词。我们可以遍历一个字符串的字符,遍历一个文件的内容,遍历目录里的所有文件,等等。这些都可以用迭代器来表达。
我在用 output_container.h 输出容器内容的时候,实际上就对容器的 beginend 成员函数返回的对象类型提出了要求。假设前者返回的类型是 I,后者返回的类型是 S,这些要求是:
I 对象支持 * 操作,解引用取得容器内的某个对象。
I 对象支持 ++,指向下一个对象。
I 对象可以和 I 或 S 对象进行相等比较,判断是否遍历到了特定位置(在 S 的情况下是是否结束了遍历)。
注意在 C++17 之前,beginend 返回的类型 I 和 S 必须是相同的。从 C++17 开始,I 和 S 可以是不同的类型。这带来了更大的灵活性和更多的优化可能性。
上面的类型 I,多多少少就是一个满足输入迭代器(input iterator)的类型了。不过,output_container.h 只使用了前置 ++,但输入迭代器要求前置和后置 ++ 都得到支持。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

C++中的迭代器是一个通用概念,允许对容器内元素进行遍历。C++17引入了更灵活的迭代器类型,包括输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器和连续迭代器。文章介绍了如何定义输入行迭代器,以及如何使用基于范围的for循环语法简化遍历输入流的代码。通过示例代码和详细解释,读者可以更好地理解迭代器的概念和在实际编程中的应用。此外,文章还提供了相关的参考资料和课后思考问题,帮助读者进一步加深对迭代器的理解和应用。 文章内容涵盖了迭代器的基本概念、类型和实际应用,对于想要深入了解C++迭代器的读者来说,这篇文章提供了丰富的信息和实用的示例。同时,通过课后思考问题,读者还可以进行进一步的思考和讨论,加深对迭代器的理解。总的来说,这篇文章对于C++编程中迭代器的理解和应用具有很高的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《现代 C++ 编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(34)

  • 最新
  • 精选
  • 小一日一
    看了老师的代码再看自己学的代码,感觉我的C++是小学生水平。 以为自己看过几遍C++ PRIMER 5th, 看过并理解effective++, more effective c++, inside the c++ object model, 能应付平时的开发需要,也能看懂公司别人的代码,就觉得自己的C++不错了,看了老师github的代码后我是彻底服了,感叹C++太博大精深,永远不敢说自己精通C++。 我什么时候才能达到老师对C++理解并使用的高度呢,难道也需要20年么?

    作者回复: 肯定还有更好的 C++ 代码的。学习无止境! 认真学习,应该不用那么久(我还没有极客时间专栏来帮助我学习呢😌)。 反过来,说明老程序员还有点价值么。🤗

    2019-12-11
    4
    29
  • Geek_b68b74
    1、使用输入行迭代器 这一部分里,“ auto&& r = istream_line_reader(is);” 这里为什么要用右值引用呢? 2、还是使用输入行迭代器这里, for (const string& line : istream_line_reader(is)) { // 示例循环体中仅进行简单输出 cout << line << endl; } “获取冒号后边的范围表达式的结果,并隐式产生一个引用,在整个循环期间都有效。注意根据生命期延长规则,表达式结果如果是临时对象的话,这个对象要在循环结束后才被销毁。” 第一句是说line在整个循环期间有效?这是想表达什么呢?还有第二句,指的是哪个临时对象呢?在哪个循环结束后销毁呢?期待您的解答

    作者回复: 你的两个疑问实际是针对同一个地方。 auto&& 那句是用一个“万能”引用捕获一个对象,左值和右值都可以。C++ 的生命期延长规则,保证了引用有效期间,istream_line_reader 这个“临时”对象一直存在。没有生命期延长的话,临时对象在当前语句执行结束后即销毁。

    2020-01-17
    3
    9
  • 千鲤湖
    过来看看老师问的那两个问题,好奇中。。。

    作者回复: 公布第 1 个问题的答案吧: #include <fstream> #include <iostream> #include "istream_line_reader.h" using namespace std; int main() { ifstream ifs{"test.cpp"}; istream_line_reader reader{ifs}; auto begin = reader.begin(); for (auto it = reader.begin(); it != reader.end(); ++it) { cout << *it << '\n'; } } 以上代码,因为 begin 多调用了一次,输出就少了一行……

    2019-12-18
    8
  • 禾桃
    输入迭代器和输出迭代器, 这个入和出是相对于什么而言的? 感觉有点绕。 谢谢!

    作者回复: cout << *it 就是读; *it = 42 就是写。

    2019-12-11
    5
  • Slience-0°C
    有个问题请教老师,工作中看到基于范围的for循环中,使用了auto &&来获取数据,而不是auto&,有啥区别么?难道是为了使用移动构造函数?伪代码如下:std::vectors<std::string> vec; for (auto&& : vec)

    作者回复: auto&& 是转发引用(第 8 讲也会简单提到)。转发引用的概念在第 3 讲里提过(我可能会在第 37 讲再展开探讨一下),但在那里的形式是 T&&。 auto&& 能匹配所有的引用(const 左值、非 const 左值、右值等),使用正确的场景下后续应该使用 std::forward。如果你没看到后续用 std::forward,那这个使用很有可能不是最好的写法。根据你是否需要修改 vec 中的内容,auto& 或 const auto& 会更加明确。

    2022-04-21
    6
    4
  • nelson
    如果stream_是nullptr会怎么样?

    作者回复: 得到一个空的不能遍历的迭代器。跟任何 end() 相等比较返回真,因而你不可以对它做 ++ 操作。如果你要硬来,它就死给你看。

    2019-12-12
    2
    3
  • EncodedStar
    课后思考 1.目前这个输入迭代器的行为,在干什么情况下可能导致意料之外的后果? 答:目前这个输入迭代器在构造里调用了++,所以,多一次构造就可能读到意料之外的结果了。 2.请尝试一下改进这个输入行迭代器,看看能不能消除这种意外,如果可以,该怎么做?如果不可以,为什么? 答:可以啊,文章里提到了,这个输入行迭代器构造的使用了++,是为了与日常使用一致,如果想改进这个一块,我们也可以改构造的时候

    作者回复: 1 对。2 你需要自己实验一下,再想想会不会有其他副作用。

    2019-12-24
    2
  • 旭东
    老师,您好,iterater中后置++的实现是不是应该返回const;避免(i++)++这样的代码通过编译?

    作者回复: 1. 不能写 const,因为你修改了自己。 2. 就算能写也防不了,因为你返回的是个全新的对象。

    2019-12-14
    2
  • 晚风·和煦
    从 C++17 开始,I 和 S 可以是不同的类型。这带来了更大的灵活性和更多的优化可能性。 没太理解这句话😂

    作者回复: 现在 r.begin() 和 r.end() 可以是不同类型了。

    2019-12-11
    2
  • doge
    第二个问题想了半天,好像做不到,根据begin的语义,拿到stream_对象后就必须取得第一行内容,否则返回的就是一个空string而不是文件的第一行。但是在iterator对象内好像没办法记录“第一次从strem_读”这样的一个状态。我尝试标记第一次,但是会忽视读的操作,这样还是会导致第一行内容的丢失。希望老师解惑。 explicit iterator(istream& is) : stream_(&is) {} iterator begin() { cout << "first_ = " << first_ << endl; if (first_) { first_ = false; return ++iterator(*stream_); } else { return iterator(*stream_); } }

    作者回复: 对的,这题的目的就是让你理解目前这种做法实际还是个不错的折中。也可以参考一下参考答案那节。

    2021-02-24
    1
收起评论
显示
设置
留言
34
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部