迭代器

到目前为止,所介绍的例子都表明 fbl:: 的多种用途 迭代器。fbl:: 中的迭代器使用的 API 与 std:: 容器,希望大家对此非常熟悉。它是 不过,有必要花些时间介绍一下 fbl:: 迭代器支持,此外它们与 std:: 迭代器。

iteratorconst_iterator

std:: 迭代器一样,fbl:: 迭代器有两种类型:非常量 和常量版本。begin()find() 之类的操作会返回 简单迭代器,以防用户拥有的容器引用 非 const,否则为 const_iterator。执行的解引用操作 在 const_iterators 上提供了 const T&,从而const可以访问 底层对象。

begin()/end()

与在 std:: 中一样,容器上的 begin() 方法会向 容器中的第一个元素,而 end() 会向 元素一超出容器中的最后一个元素。开头和结尾都会 对对 const 引用调用时自动返回 const_iterator, 但 cbegin()/cend() 如果需要 明确需要 const_iterator 从对 容器。

迭代器比较和默认初始化迭代器与 end()

std:: 一样,所有 fbl:: 迭代器均支持使用 == 测试相等性 和 != 运算符。与 std:: 不同,没有迭代器具有随机访问权限 迭代器语义,并且不支持 >>=<<= 运算符 针对任何 fbl:: 容器的迭代器类型。

此外,默认初始化的 调用容器的 end() 方法返回的迭代器和迭代器。 它们在语义上是相同的。默认初始化的迭代器和 end() 的值无效,因此两者之间的比较将返回 true。 不过,两个实例中包含的位有所不同。始终使用 比较运算符。

fbl::DoublyLinkedList<Obj*> the_list;
fbl::DoublyLinkedList<Obj*>::iterator default_init;
auto end_init = the_list.end();

if (default_init == end_init) { }                            // This comparison is true
if (!memcmp(&default_init, &end_init, sizeof(end_init))) { } // This is not.

迭代器推进

所有迭代器都支持 ++ 运算符的前缀和后置形式。通过 会将迭代器移到序列中的下一个元素, 返回指向下一个元素的迭代器副本。后缀 形式会在返回结果时将迭代器移至序列中的下一个元素 预高级迭代器的副本。

// Assuming that the list starts containing objects with values
// "5 7 9", in that order.
fbl::DoublyLinkedList<Obj*> the_list;
auto iter   = the_list.begin();   // iter points to "5".
auto second = iter++;             // iter points to "7", but second points to "5"
auto third  = ++iter;             // iter points to "9", and so does third
++iter;                           // iter is now equal to the_list.end()

具有双重关联列表存储分区的 DoublyLinkedListHashTable 的迭代器。 和 WAVLTree 都支持通过 -- 运算符进行双向迭代, 。SinglyLinkedListHashTable 单个关联列表存储分区不会。

让迭代器前进到容器末尾之后会得到 container.end()。 试图进一步推进是合法的,但 迭代器。备份当前设置为 使用 -- 运算符的 container.end() 将生成一个迭代器,该迭代器指向 添加到列表中的最后一个元素,但备份之前 默认不会进行初始化。而是对++-- 默认初始化状态会使迭代器保持默认初始化状态。 最后,备份值等于 container.begin() 的迭代器, 生成值等于 container.end() 的迭代器。后续 -- 的应用将按从左到右的顺序依次浏览各元素, 最后一个元素。

解引用迭代器

fbl:: 容器中的元素始终是对象,因此它们始终支持 -> 运算符以及一元 * 运算符。两者都会产生 T&const T&-> 随后会访问其成员),具体取决于 迭代器是否为 const_iterator

尝试遵循无效的迭代器是违法行为,并且会导致 就会触发 ZX_DEBUG_ASSERT 或未定义的行为,具体取决于 和构建。

使用 container::make_iterator() 从对象创建迭代器

由于容器具有侵扰性,因此您可以创建 容器迭代器。例如,假设 一个按键排序的对象树,一个接受对象并返回 按键序列中对它前面对象的引用,可以用 例如:

using ObjectTree = fbl::WAVLTree<uint64_t, fbl::RefPtr<Object>>;

fbl::RefPtr<Object> FetchPrior(ObjectTree& tree, Object& obj) {
  ZX_DEBUG_ASSERT(obj.InContainer());
  auto iter = tree.make_iterator(obj);
  return (--iter).IsValid() ? iter.CopyPointer() : nullptr;
}

iterator::IsValid()

可以使用 IsValid 测试所有 fbl:: 迭代器实例的有效性 方法。在此测试中 方式等同于测试 iter != container.end(),但 IsValid 方法生成的代码效率会稍微有些高,具体取决于 编译器是什么样子的,以及它对 容器的 end() 方法。

iterator::CopyPointer()

最后,fbl:: 迭代器提供了一个名为 CopyPointer 的方法,该方法可 用于生成容器所使用的指针类型的副本。对于 原始指针容器,没什么特别之处。它只是T* 指向该对象的指针。实际上,iter.CopyPointer() == &(*iter) 应始终 为 true。

CopyPointer 不适用于具有唯一语义的托管指针。正在尝试 对包含使用 std::unique_ptr 跟踪的对象容器调用 CopyPointer 会产生错误。

最后,当对以下容器的迭代器执行 CopyPointer 时, 可复制的托管指针,系统将使用 复制指针类型的构造函数。也就是说,它会生成一个新的 对象的托管引用。

尝试对指向可复制指针的无效迭代器调用 CopyPointer 类型会生成 nullptr