文章目录
侵入式双向链表
在libgo中设计了一套侵入式双向链表,用来保存协程任务,调度队列等,所以今天来分析一下这个双向链表的设计,方便我们之后对libgo其他模块的分析。
TSQueueHook
想要这个双向链表,那么链表保存的类型必须继承TSQueueHook,UML类图如下:
struct TSQueueHook
{
//上一个节点
TSQueueHook* prev = nullptr;
//下一个节点
TSQueueHook* next = nullptr;
//可选的检查节点
void *check_ = nullptr;
//将theNext设置为该节点的下一个节点
ALWAYS_INLINE void link(TSQueueHook* theNext) {
assert(next == nullptr);
assert(theNext->prev == nullptr);
next = theNext;
theNext->prev = this;
}
//取消与theNext节点的关联
ALWAYS_INLINE void unlink(TSQueueHook* theNext) {
assert(next == theNext);
assert(theNext->prev == this);
next = nullptr;
theNext->prev = nullptr;
}
};
SList
SList是一个类模板,定义时会进行静态断言检测T是否为TSQueueHook的子类,所以一定得先继承TSQueueHook再使用SList
static_assert((std::is_base_of<TSQueueHook, T>::value), "T must be baseof TSQueueHook");
整体的UML类图如下:
Attribute
SList的成员变量:
- head_ : 头节点
- tail_ : 尾结点
- count_ : 链表中节点数量
Method
SList(SList && other)
//移动构造函数
SList(SList<T> && other)
{
//浅拷贝
head_ = other.head_;
tail_ = other.tail_;
count_ = other.count_;
//将other设为空链表
other.stealed();
}
void append(SList &&other)
将一个SList移动进来
void append(SList<T> &&other)
{
//添加进来的SList为空则直接返回
if (other.empty())
return;
//该SList为空
if (empty())
{
//调用移动赋值函数
*this = std::move(other);
return;
}
//将other的头结点链接到this的尾结点
tail_->link(other.head_);
//重新设置尾结点
tail_ = other.tail_;
//增加节点数量
count_ += other.count_;
//将other设为空链表
other.stealed();
}
void erase(T *ptr)
删除一个节点,SList并不管理节点的生命周期,erase只是将节点从SList去除,并不会释放节点内存,libgo中有实现一套引用计数体系,若使用了引用计数,则不需要担心生命周期的管理