到目前为止,所介绍的例子都表明 fbl::
的多种用途
迭代器。fbl::
中的迭代器使用的 API 与
std::
容器,希望大家对此非常熟悉。它是
不过,有必要花些时间介绍一下
fbl::
迭代器支持,此外它们与
std::
迭代器。
iterator
和const_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()
具有双重关联列表存储分区的 DoublyLinkedList
、HashTable
的迭代器。
和 WAVLTree
都支持通过 --
运算符进行双向迭代,
。SinglyLinkedList
和 HashTable
单个关联列表存储分区不会。
让迭代器前进到容器末尾之后会得到 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
。