本篇继续讲解 Project 2:B+ 树的实现。让我们先从相对简单的迭代器实现开始,然后讲述删除的实现。因为删除部分篇幅较长,并发控制我们放到下一篇再讲。
迭代器(Iterator)
熟悉 C++ 的同学们应该知道,迭代器(Iterator)是 STL 中非常重要的一个概念,它将容器与对容器的操作解耦,容器提供 begin()
,end()
等返回迭代器的函数,而算法直接依托这些迭代器进行操作,不再附属于容器本身。其设计也与传统的数组(指针)兼容(++, --
移动,==, !=
判断,*, ->
解引用)。这里就是让我们为 B+ 树实现一个迭代器。
先来看 BPlusTree
类中的接口,一共有三个函数要实现:Begin()
,Begin(const KeyType &key)
和 End()
,其中第二个的意思是找到下界为 key
的位置。bustub 也为我们定义了迭代器的类型:
图里是我修改后的状态,下面来讲实现的思路。B+ 树的所有数据存在于最下层的叶节点,迭代器也是在叶节点内和叶节点之间移动。因此,一个迭代器的位置由两部分组成:在哪个叶节点(用其编号和指针表示,二者同步变化)和在叶节点中第几个位置(用索引表示,即 index_in_leaf_
成员)。于是,迭代器相等的判断就是叶节点编号相同且叶内索引相同。由于迭代器会在不同节点间移动,它需要有能够获取和返还 page 的能力,所以将 buffer_pool_manager_
也作为一个它的成员,在构造函数中由外部传入。规定 page_id_
为 INVALID_PAGE_ID
表示无效迭代器(即 End()
返回的迭代器)。实现上唯一需要注意的就是 operator++()
中跳页的处理,如下:
Tips:注意这个
operator++()
对应的是前加,也就是++it
的情况,所以先做操作,再返回*this
。关于 C++ 操作符重载可以参考这个链接:What are the basic rules and idioms for operator overloading?
回到 BPlusTree
类,实现三个函数。首先 Begin()
,一路向下找到最左边的叶节点即可:
第二个 Begin(const KeyType &key)
,我们已经实现了 GetLeafPage()
,所以已经能够找到目标页,还需要找到目标索引,不妨在 LeafPage
类中加一个 LowerBound()
函数:
End()
函数就