C++基础(十三):再探迭代器 (插入迭代器、流迭代器、反向迭代器、移动迭代器)

本文为《C++ Primer》的读书笔记

#include <iterator>
  • 插入迭代器 (insert iterator): 这些迭代器被绑定到一个容器上, 可用来向容器插入元素
  • 流迭代器 (stream iterator): 这些迭代器被绑定到输入或输出流上,可用来遍历所关联的 IO 流
  • 反向迭代器 (reverse iterator) : 这些迭代器向后而不是向前移动。除了 forward_list 之外的标准库容器都有反向迭代器
  • 移动迭代器 (move iterator): 这些专用的迭代器不是拷贝其中的元素, 而是移动它们

插入迭代器

  • 插入器是一种迭代器适配器, 它接受一个容器, 生成一个迭代器, 能实现向给定容器添加元素。当我们通过一个插入迭代器进行赋值时, 该迭代器调用容器操作来向给定容器的指定位置插入一个元素
    在这里插入图片描述
  • 插入器有三种类型, 差异在于元素插入的位置:
    • back_inserter 接受一个指向容器的引用, 返回一个与该容器绑定的插入迭代器。当我们通过此迭代器赋值时, 赋值运算符会调用 push_back 将一个具有给定值的元素添加到容器中
    • front_inserter 创建一个使用 push_front 的迭代器
    • inserter 创建一个使用 insert 的迭代器。此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。元素将被插入到给定迭代器所表示的元素之前

只有在容器支持 push_front / push_back / insert 的情况下,我们才可以使用 front_inserter / back_inserter / inserter

vector<int> vec; 
auto it = back_inserter(vec); //通过它赋值会将元素添加到vec中
*it = 42; // vec 中现在有一个元素, 值为 42
// 假设 `it` 是由 `inserter` 生成的插入迭代器
*it = val;

// 等价代码
it = c.insert(it, val); // it 指向新加入的元素
++it; // 递增 it 使它指向原来的元素

iostream 迭代器

  • 虽然 iostream 类型不是容器, 但标准库定义了可以用于这些 IO 类型对象的迭代器。这些迭代器将它们对应的流当作一个特定类型的元素序列来处理。通过使用流迭代器, 我们可以用泛型算法从流对象读取数据以及向其写入数据
    • istream_iterator 读取输入流
    • ostrearn_iterator 向一个输出流写数据

istream_iterator 操作

在这里插入图片描述

  • 当创建一个流迭代器时, 必须指定迭代器将要读写的对象类型istream_iterator 要读取的类型必须定义了输入运算符。当创建一个 istream_iterator 时, 我们可以将它绑定到一个流
  • 当然, 我们还可以默认初始化迭代器, 这样就创建了一个可以当作尾后值使用的空迭代器: 一旦其关联的流遇到文件尾或遇到 IO 错误, 迭代器的值就与尾后迭代器相等
ifstream in("afile");
istream_iterator<string> str_it(in); // 从 "afile" 读取字符串
istream_iterator<int> in_iter(cin); // 从 cin 读取 int
istream_iterator<int> eof; 			// istream 尾后迭代器
while (in_iter != eof)
	// 后置递增运算读取流,返回迭代器的旧值
	// 解引用迭代器、获得从流读取的前一个值
	vec.push_back(*in_iter++);

// 我们可以将程序重写为如下形式, 这体现了 `istream_iterator` 更有用的地方:
istream_iterator<int> in_iter(cin), eof; 
vector<int> vec(in_iter, eof); // 从迭代器范围构造 vec

istream_iterator 允许使用懒惰求值

  • 当我们将一个 istream_iterator 绑定到一个流时,标准库并不保证迭代器立即从流读取数据。具体实现可以推迟从流中读取数据, 直到我们使用迭代器时才真正读取。标准库中的实现所保证的是,在我们第一次解引用迭代器之前, 从流中读取数据的操作已经完成了
  • 对于大多数程序来说,立即读取还是推迟读取没什么差别。但是,如果我们创建了一个 istream_iterator, 没有使用就销毁了, 或者我们正在从两个不同的对象同步读取同一个流,那么何时读取可能就很重要了

ostream_iterator 操作

在这里插入图片描述

  • 我们可以对任何具有输出运算符<< 的类型定义ostream_iterator。当创建一个 ostream_iterator 时,我们可以提供(可选的) 第二参数, 它是一个 C 风格字符串,在输出每个元素后都会打印此字符串

不允许空的或表示尾后位置的 ostream_iterator

// 输出值的序列
ostream_iterator<int> out_iter(cout, " ");
for (auto e : vec)
	*out_iter++ = e; 
cout << endl;
  • 值得注意的是, 当我们向out_iter赋值时, 可以忽略解引用和递增运算即,循环可以重写成下面的样子。但是, 推荐第一种形式。在第一种写法中,流迭代器的使用与其他迭代器的使用保持一致
for (auto e : vec)
	out_iter = e; // 赋值语句将元素写到 cout
cout << endl;

  • 如果类类型既有输入运算符也有输出运算符, 就可以使用 IO 迭代器:
istream_iterator<Sales_item> item_iter(cin), eof;
ostream_iterator<Sales_item> out_iter(cout, "\n");

// 将第一笔交易记录存在 sum 中, 并读取下一条记录
Sales_item sum = *item_iter++;
while (item_iter != eof) {
	if (item_iter->isbn() == sum.isbn())
		sum += *item_iter++; 	// 将其加到 sum 上并读取下一条记录
	else (
		out_iter = sum;			// 输出 sum 当前值
		sum = *item_iter++;		// 读取下一条记录
	}
}
out_iter = sum;					// 打印最后一组记录的和

反向迭代器

  • 反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。递增一个反向迭代器会移动到前一个元素; 递减一个迭代器会移动到下一个元素
  • 除了forward_list 之外,其他容器都支持反向迭代器。我们可以通过调用 rbeginrendcrbegincrend 成员函数来获得反向迭代器。这些成员函数返回指向容器尾元素和首元素之前一个位置的迭代器
    在这里插入图片描述
// 逆序打印 `vec` 中的元素
vector<int> vec = {0,1,2,3,4,5,6,7,8,9};
// 从尾元素到首元素的反向迭代器
for(auto r_iter = vec.crbegin(); r_iter != vec.crend(); ++r_iter)
	cout << *r_iter << endl;

反向迭代器需要递减运算符

  • 我们只能从既支持 ++ 也支持 -- 的迭代器来定义反向迭代器。毕竟反向迭代器的目的是在序列中反向移动
  • 除了forward_list之外, 标准容器上的其他迭代器都既支持递增运算又支持递减运算。但是, 流迭代器不支持递减运算, 因为不可能在一个流中反向移动。因此, 不可能从一个forward_list或一个流迭代器创建反向迭代器

反向迭代器和其他迭代器间的关系

// line 为一个保存着一个逗号分隔的单词列表
// 打印 line 中的第一个单词
auto comma = find(line.cbegin(), line.cend(), ',');
cout << string(line.cbegin(), comma) << endl;
// 打印最后一个单词

// 在一个逗号分隔的列表中查找最后一个元素
// 如果 line 中有逗号, 则 rcomma 指向最后一个逗号。如果 line  中没有逗号, 则 rcomma 指向 line.crend()
auto rcomma = find(line.crbegin(), line.crend(), ',');

// 错误:将逆序输出单词的宇符
cout << string(line.crbegin(), rcomma) << endl;

在这里插入图片描述

  • 上图说明了问题所在: 我们使用的是反向迭代器,会反向处理string。而我们希望按正常顺序打印, 因此不能直接使用 rcomma。因为它是一个反向迭代器,意味着它会反向朝 string 的开始位置移动。需要做的是,rcomrna 转换回一个普通迭代器, 能 在line 中正向移动。我们通过调用reverse_iteratorbase 成员函数来完成这一转换, 此成员函数会返回其对应的普通迭代器:
// 正确:得到一个正向迭代器, 从逗号开始读取字符直到line末尾
cout << string(rcomma.base(), line.cend()) << endl;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ std::map迭代器是用来遍历map容器中的元素的工具。迭代器可以指向map容器中的每个键值对,允许访问键和值。在C++中,有三种常用的迭代器类型:基础迭代器反向迭代器和常量迭代器基础迭代器是通过`begin()`和`end()`函数来获取的。`begin()`函数返回指向map容器中第一个元素的迭代器,而`end()`函数返回指向map容器中最后一个元素之后位置的迭代器基础迭代器可以使用自增运算符`++`来遍历map容器中的键值对,并通过解引用(*)操作符来获得键和值。例如,使用`map<string, int>::iterator`类型定义迭代器,并通过循环遍历map容器中的键值对,可以实现对map容器的遍历。 反向迭代器是通过`rbegin()`和`rend()`函数来获取的。`rbegin()`函数返回指向map容器中最后一个元素的迭代器,而`rend()`函数返回指向map容器中第一个元素之前位置的迭代器反向迭代器可以使用自减运算符`--`来遍历map容器中的键值对,并通过解引用(*)操作符来获得键和值。反向迭代器可以实现对map容器的反向遍历。 常量迭代器是通过`cbegin()`和`cend()`函数来获取的。常量迭代器基础迭代器类似,但是不能修改map容器中的元素。常量迭代器可以用于在不改变map容器的情况下遍历元素。 综上所述,C++ std::map迭代器是用于遍历map容器中的键值对的工具。基础迭代器反向迭代器和常量迭代器分别用于正向遍历、反向遍历和只读遍历map容器。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++ std::map用迭代器遍历删除遇到的坑](https://blog.csdn.net/zidian666/article/details/125673395)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++map迭代器最全最容易理解](https://blog.csdn.net/qq_53547805/article/details/122146122)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值