leveldb提供了迭代器遍历的功能,并提供了迭代器的接口。这也是一个纯虚类的接口,leveldb中任何集合类型对象的迭代器都基于这个纯虚类实现。
class Iterator {
public:
Iterator();
virtual ~Iterator();
Iterator(const Iterator&) = delete;
Iterator& operator=(const Iterator&) = delete;
virtual bool Valid() const = 0;
virtual void SeekToFirst() = 0;
virtual void SeekToLast() = 0;
virtual void Seek(const Slice& target) = 0;
virtual void Next() = 0;
virtual void Prev() = 0;
virtual Slice key() const = 0;
virtual Slice value() const = 0;
virtual Status status() const = 0;
};不过因为迭代器接口的实现类也是一个对象,在对象结束的时候也需要使用一些资源,对象的资源也需要释放。为了让简化用户的任务量,将这个资源的释放在这个父类的析构函数实现,提供了注册函数,只要实现者将这些函数注册,那么就能够在对象生命周期结束的时候自动释放,而不需要开发者耗费精力何时去释放内存。
using CleanupFunction = void(*)(void* arg1, void* arg2);
void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2);内部由一个链表,记录着这些清理函数,对象释放的时候执行这些清理函数。
class Iterator {
....
private:
struct CleanupNode {
bool IsEmpty() const { return function == nullptr; }
void Run() {
assert(function != nullptr);
(*function)(arg1, arg2);
}
CleanupFunction function;
void* arg1;
void* arg2;
CleanupNode* next;
};
CleanupNode cleanup_head_;
}在析构函数中调用这些清理函数:
Iterator::~Iterator() {
// cleanup_head_是预留的一个头节点,通过它是否设置了清理函数来判断是否需要进行清理
// 头节点后面的节点如果存在那么肯定是存在清理函数的,执行并删除这个节点
if (!cleanup_head_.IsEmpty()) {
cleanup_head_.Run();
for (CleanupNode* node = cleanup_head_.next; node != nullptr;) {
node->Run();
CleanupNode* next_node = node->next;
delete node;
node = next_node;
}
}
}清理函数的注册如下:
void Iterator::RegisterCleanup(CleanupFunction function, void* arg1, void* arg2) {
// 保证注册的清理函数一定存在
assert(function != nullptr);
CleanupNode* node;
// 第一个清理函数可以利用头节点,后面的利用带头节点的头插法来进行链表的插入
if (cleanup_head_.IsEmpty()) {
node = &cleanup_head_;
} else {
node = new CleanupNode;
node->next = cleanup_head_.next;
cleanup_head_.next = node;
}
node->function = function;
node->arg1 = arg1;
node->arg2 = arg2;
}这种设计既高度抽象,有提高了代码的复用,非常值得学习。