不同的容器,根据底层数据结构不同,遍历方式自然也不同:
for (std::size_t i = 0; i != a.size(); ++i)
do_something(a[i]);
// 可能的方式:通过指向结点的“句柄”(指针)遍历一个链表
for (node_handle node = l.first_node(); node; node = node.next())
do_something(node.value())
如果是个更复杂的容器呢,比如基于哈希表/红黑树实现的关联容器?
所有容器都有其对应的迭代器类型 Container::iterator,例如 std::string::iterator, std::vector<int>::iterator。
所有容器都支持 c.begin(), c.end(),分别返回指向首元素和指向尾后位置的迭代器。
* 我们总是使用左闭右开区间 [begin, end) 表示一个“迭代器范围”
it1 != it1 比较两个迭代器是否相等(指向相同位置)++it 让 it 指向下一个位置。*it 获取 it 指向的元素的引用。for (auto it = c.begin(); it != c.end(); ++it)
do_something(*it);
基于范围的 for 语句:完全等价于上面的使用迭代器的遍历
for (auto &x : c)
do_something(x);
const_iterator带有“底层 const”的迭代器。
Container::const_iteratorc.cbegin(), c.cend()const 对象上,c.begin() 和 c.end() 也返回 const_iterator。对 const_iterator 解引用会得到 const T & 而非 T &,无法通过它修改元素的值。
begin, end, cbegin, cend再次出现了 const 和 non-const 的重载。
class MyContainer {
public:
using iterator = /* unspecified */;
using const_iterator = /* unspecified */;
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
};
*it, it->mem, ++it, it++, it1 == it2, it1 != it2--it, it--。it[n], it + n, it += n, n + it, it - n, it -= n, it1 - it2, <, <=, >, >=。vector, string, array, deque 的迭代器是 RandomAccessIterator;list 的迭代器是 BidirectionalIterator;forward_list 的迭代器是 ForwardIterator。
还有两种迭代器型别:InputIterator 和 OutputIterator。
InputIterator 表示可以通过这个迭代器获得元素(不要求能修改它所指向的元素)
OutputIterator 表示可以通过这个迭代器写入元素(不要求能获得它所指向的元素)
A ForwardIterator is an InputIterator.
稍后我们会看到一些例子。
std::advance(iter, n) 将 iter 前进 n 步。 n 也可以是负的。std::distance(iter1, iter2) 返回 iter 和 iter2 之间的距离:相距几个元素。std::next(iter) 返回 iter 的“下一个位置”, std::prev(iter) 返回 iter 的“上一个位置”,它们都不会改变 iter 本身。
auto tmp = iter; ++tmp; 。以上函数对于各类迭代器型别都有支持,并且会针对不同的型别提供不同的实现。
iter1 和 iter2 是什么型别,你都可以用 std::distance(iter1, iter2) 获取它们的距离,不用担心 iter1 - iter2 这个表达式能不能编译之类的问题。一种用起来像迭代器的东西
++ 实际上是 --*iter 返回右值引用*iter++ = x 会被“变”成 c.push_back(x)x = *iter++ 被“变”成 std::cin >> xreverse_iterator每个容器都有自己的反向迭代器: Container::reverse_iterator 和 Container::const_reverse_iterator ,通常被这样定义:
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
c.rbegin(), c.rend(), c.crbegin(), c.crend()++ 和 -- 在反向迭代器上都是反的。std::vector v{1, 2, 3, 4, 5};
for (auto rit = v.rbegin(); rit != v.rend(); ++rit)
std::cout << *rit << ' ';
输出:5 4 3 2 1
move_iteratorstd::make_move_iterator(iter) 从一个普通的迭代器 iter 变出一个“移动迭代器”*mit 会得到右值引用而非左值引用,从而元素更可能被移动而非被拷贝。std::vector<std::string> words = someValues();
std::vector<std::string> v(words.size());
std::copy(std::make_move_iterator(words.begin()),
std::make_move_iterator(words.end()), v.begin());
words 中的每个 string 被移动进了 v,而不是拷贝。
对于一个给定的迭代器类型 Iter , std::reverse_iterator<Iter> 和 std::move_iterator<Iter> 都是一个新的迭代器类型:
Iter 的“wrapper”:在内部保存一个 Iter 类型的原始迭代器,可以通过 rit.base() 访问。Iter 的接口定义自己的接口: ++rit 会调用 --it , *mit 会返回 std::move(*it)C++23 还有个 std::basic_const_iterator<Iter> ,用类似的方式定义了 Iter 的带有底层 const 的版本。
你当然可以用类似的方式实现自己的特殊迭代器。
insert_iterator, front_insert_iterator, back_insert_iterator
典型的 OutputIterator:
*iter 写入元素,不能从 *iter 读取元素insert, push_front 或 push_back,将“写入”的元素插入容器std::vector<int> numbers = someValues();
std::vector<int> v;
std::copy(numbers.begin(), numbers.end(), v.begin()); // 错误!
std::copy(numbers.begin(), numbers.end(), std::back_inserter(v)); // 正确
std::back_inserter(c) 生成一个 std::back_insert_iterator<Container>,它内部保存一个 Container &r = c ,会不断调用 r.push_back(x) 将向它“写入”的元素添加进 c。
读入一串数存进一个 vector<int>:
std::vector<int> v(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>{});
利用 std::copy 将 v 中的元素输出,并且每个后面跟一个 ", ":
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, ", "));
istream_iterator 是一种 InputIterator,它不断从输入流中获取元素ostream_iterator 是一种 OutputIterator,它不断将向它“写入”的元素写进输出流InputIterator 和 ForwardIterator 都要求支持 ++it, it++, *it, it->mem, ==, !=。
这两类迭代器的区别究竟是什么?
InputIterator 和 ForwardIterator 都要求支持 ++it, it++, *it, it->mem, ==, !=。
这两类迭代器的区别究竟是什么?—— ForwardIterator 提供 multi-pass guarantee
auto original = iter; // 对当前的 iter 做个拷贝
auto value = *iter; // 现在 iter 指向的元素是 value
++iter;
assert(*original == value);
对于一个 ForwardIterator 来说,original 指向了 iter 在递增之前指向的位置,那个位置上的值始终是 value。
InputIterator 不这么认为,它只保证能“input”:++iter 就意味着我们打算获取“下一个值”了,先前的值也就无法再被获取了。(考虑“输入”的过程)