开始使用

本部分将展示一个使用中的侵入式容器的简单示例, 并花少量时间讨论一些基本原则 操作。虽然本指南的后续部分将会详细介绍 以及许多其他概念,本课程将介绍一些基本概念, 演示:

  1. 指针类型以及如何使用指针控制对象的生命周期
  2. 包含的“mix-ins”以及如何使对象存在于 容器。
  3. 如何对 fbl:: 侵入式容器中的元素进行迭代
  4. 如何从 fbl:: 侵入式容器中移除元素
  5. fbl:: 侵入式容器中设置 build 依赖项

简单示例

我们先从一个简单的例子开始。在此示例中, 将被定义为可存在于双重链接列表中,使用 std::unique_ptr<>。这些对象的列表将如下所示:

  1. 已填充
  2. 迭代以输出对象的子集
  3. 通过迭代来移除一部分对象
  4. 已明确清理
#include <stdint.h>
#include <fbl/intrusive_double_list.h>

// An object that holds an immutable int and can exist on a doubly linked list
// managed using a std::unique_ptr
class MyObject : public fbl::DoublyLinkedListable<std::unique_ptr<MyObject>> {
 public:
  explicit MyObject(int val) : val_(val) {}

  int val() const { return val_; }

 private:
  const int val_;
};

extern void TakeThisObjectFromMe(std::unique_ptr<MyObject>);

void DoThings() {
  fbl::DoublyLinkedList<std::unique_ptr<MyObject>> list;

  // Add 100 random integers to our list.
  for (uint32_t i = 0; i < 100; ++i) {
    list.push_back(std::make_unique<MyObject>(rand()));
  }

  // print out any members of the list that are even
  for (const auto& obj : list) {
    if (!(obj.val() % 2)) {
      printf("Even Object %d\n", obj.val());
    }
  }

  // Remove any objects that are divisible by 7 and give them to someone else.
  for (auto iter = list.begin(); iter != list.end(); ) {
    auto consider = iter++;
    if (!(consider->val() % 7)) {
      TakeThisObjectFromMe(list.erase(consider));
    }
  }

  // Destroy the rest of the object by forcing the list to release its unique
  // references to the objects. We could also simply let the list leave the
  // scope of the function, which would do the same.
  list.clear();
}

指针类型

首先要注意的是 std::容器和fbl::容器是指fbl::容器始终 使用指针跟踪对象具体而言,它们是使用一组 明确支持的指针类型。定义了 fbl:: 列表的类型后, 它定义为指向某个对象类型的特定指针类型的列表。在本课中, 选择 std::unique_ptr<>,这意味着 对象可以存储在本地,也可以由列表持有,但不能被 两种类型。目前有 3 种不同的指针类型, fbl:: 个容器允许:

  • T*:一种没有托管 RAII 语义的原始指针类型。
  • std::unique_ptr<T, Deleter>:标准的唯一指针类型,即具有 自定义删除程序,或使用默认删除程序。
  • fbl::RefPtr<T>fbl:: 侵入性引用指针。基本上是 fbl::std::shared_ptr<T> 的侵入性版本。

必须告诉所有容器,它们存储的是哪种类型的对象 通过 容器类型使用托管指针类型时,容器始终会 将对象添加到容器时拥有引用的所有权,并授予 从容器中移除对象时重新获得该引用的所有权。 此规则的例外情况为 clear(),它会删除所有引用。

“原始”指针没有特殊的所有权语义,这意味着 确保对象在容器中保持活跃状态; 从容器中移除或清除时,此类库应该得到妥善清理。

可列出/可包含的 Mix-in 类

如果对象必须将下一个/上一个指针存储在某个位置,其中 此节点状态是否有效?列表如何找到该状态?实际上还有 多种方式来控制此行为,但此示例演示了 最简单方法。这是根据 DoublyLinkedListListable Mix-in 辅助程序。就像列表本身一样,您需要告知 通过第一个模板混入对象类型和指针类型 参数。这些类型需要与为列表定义指定的类型匹配 本身。默认情况下,列表会寻找其 Listable 的实例 包含对象派生来源的混杂内容,以便找到 记账。所有这些都在编译时进行,没有运行时开销 涉及在对象中查找节点状态的过程。

迭代

fbl:: 容器既支持基于范围的 for 循环迭代,也支持 更经典的 begin()/end() 迭代样式。与 std:: 容器一样,当 使用基于范围的 进行枚举,则对象的 l 值引用将是 返回。请务必说出“auto&”或“const auto&” (甚至 MyObject&),而不是简单地使用 auto,以免最终导致 意外触发对象的复制构造函数。

fbl:: 容器迭代器还支持标准的前缀和后缀形式 ++ 运算符的实现,您可以看到用于选择性地移除元素 从列表中删除本例中使用了后缀形式 consider 会变为可选择移除的元素,而 iter 会变为指向列表中需要考虑的下一个元素的指针。

当元素位于 。底层对象可通过所有标准方式访问 -> 运算符,或使用 (*iter).val() 样式。原始指针可以 甚至可以通过使用 &(*iter) 来获取该对象。需要注意 因为它们是指向对象的原始指针。不建议 可以将其存储任意时长,尤其是在您使用新型 可控制对象生命周期的托管指针。

移除元素

在此示例中,移除元素后,系统将使用迭代器将它们移除 执行擦除操作清除操作的结果将是 列表使用的指针类型。参考文件可能已被撤回 并存储在本地指针实例中 调用某个外部函数,将 对象发送到进程中的外部函数。

clear() 是另一种从容器中移除元素的方法,但它会丢弃 所有元素引用,可能会破坏它们指向的对象 。

设置 build 依赖项

若要使用 fbl:: 容器,用户必须确保均包含 为他们要使用的容器提供适当的头文件,并加入 依赖于 fbl 库(位于项目的 BUILD.gn 文件中)。

每种容器类型必需的包含文件如下:

容器类型 include 语句
fbl::SinglyLinkedList<> #include <fbl/intrusive_single_list.h>
fbl::DoublyLinkedList<> #include <fbl/intrusive_double_list.h>
fbl::WAVLTree<> #include <fbl/intrusive_wavl_tree.h>
fbl::HashTable<> #include <fbl/intrusive_hash_table.h>

用户还必须将库 //zircon/system/ulib/fbl 添加到 deps 或项目的 BUILD.gn 文件的 public_deps 部分。参考 位于 deps 部分(当用户编写 可执行文件,或者仅在其库的不公开部分使用 fbl。如果 fbl 在用户库的公开头文件中的任意位置使用, 应改用 public_deps