到目前为止,所介绍的例子都表明 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。