本部分将展示一个使用中的侵入式容器的简单示例, 并花少量时间讨论一些基本原则 操作。虽然本指南的后续部分将会详细介绍 以及许多其他概念,本课程将介绍一些基本概念, 演示:
- 指针类型以及如何使用指针控制对象的生命周期
- 包含的“mix-ins”以及如何使对象存在于 容器。
- 如何对
fbl::
侵入式容器中的元素进行迭代。 - 如何从
fbl::
侵入式容器中移除元素。 - 从
fbl::
侵入式容器中设置 build 依赖项。
简单示例
我们先从一个简单的例子开始。在此示例中,
将被定义为可存在于双重链接列表中,使用
std::unique_ptr<>
。这些对象的列表将如下所示:
- 已填充
- 迭代以输出对象的子集
- 通过迭代来移除一部分对象
- 已明确清理
#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 类
如果对象必须将下一个/上一个指针存储在某个位置,其中
此节点状态是否有效?列表如何找到该状态?实际上还有
多种方式来控制此行为,但此示例演示了
最简单方法。这是根据 DoublyLinkedList
的
Listable
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
。