本节将介绍base公共通用库中的containers,其包含堆栈、列表、集合、以及Most Recently Used cache(最近使用缓存模板)。
linked_list.h:一个简单的列表类型,通过模板实现,内部采用双链表的形式,有区别于c++标准模板库的std::list<T*>,它的使用方式为:base::LinkedList<T>;
相对std::list<T*>,其优点有:
1. 删除一个元素,操作复杂度为O(1),而std::list<T*>为O(n),因其内部需要获取一个T*的元素的迭代器;
2. 插入一个元素不需要堆分配器。
该模板链表其内置节点:base::LinkNode<T>;链表:base::LinkedList<T>
节点示例:
class MyNodeType : public base::LinkNode<MyNodeType>
{
};
创建链表实例示例:LinkedList<MyNodeType> list;
具体使用示例:
1 class Node : public LinkNode<Node> 2 { 3 public: 4 5 explicit Node(int id) : id_(id) {} 6 7 int id() const { return id_; } 8 9 private: 10 11 int id_; 12 };
1 LinkedList<Node> list; 2 Node n1(1); 3 Node n2(2); 4 Node n3(3); 5 Node n4(4); 6 Node n5(5); 7 list.Append(&n1); 8 list.Append(&n2); 9 list.Append(&n3); 10 list.Append(&n4); 11 list.Append(&n5); 12 13 n3.RemoveFromList(); 14 15 list.Append(&n3); 16 n3.InsertBefore(&n2); 17 n4.InsertAfter(&n1); 18 19 for (const LinkNode<Node>* node = list.head(); node != list.end(); node = node->next() ) 20 { 21 printf("id = %d," ,node->value()->id() ); 22 } 23 24 for (const LinkNode<Node>* node = list.tail(); node != list.end(); node = node->previous() ) 25 { 26 printf("id = %d," ,node->value()->id() ); 27 }
以上代码包含,节点自定义类型、链表对象定义、创建,节点追加、节点移除、节点再追加、节点插入、移动、前向遍历、后向遍历;
执行到第12行时,链表内容为:1,2,3,4,5;执行13行后:1,2,4,5;执行15行后:1,2,4,5,3;执行16行后:1,3,2,4,5(暂不执行该行);执行17行后:1,4,3,2,5;
前向遍历:id = 1,id = 4,id = 3,id = 2,id = 5;后向遍历:id = 5,id = 2,id = 3,id = 4,id = 1.;
似乎实际上与预计结果不一致,请注意:第15行、16行其实会引入BUG,导致遍历产生无限循环,使用时避免出现相同元素地址被写入链表的情况;
总结链表:
节点LinkNode<T>含LinkNode<T>* previous_和LinkNode<T>* next_指针成员,分别保存指向当前元素的前一个和后一个元素地址,base::LinkedList<T>中含成员LinkNode<T> root_,为整个链表的根节点,也作为遍历中最后一个节点的指示灯;维护链表,并采用后向插值的方式追加元素至链表。
基本上实现比较简单,相对std::list更快速且基于元素地址,但也可能因不谨慎的操作引入上面的BUG。
stack_container.h:分配器StackAllocator,采用继承于std::allocator<T>;其内部维护一个原始申请堆缓冲区对象StackAllocator::Source,用以为分配器备份存储内容,减少再次在堆中申请或其他操作,其他采用该分配器可以共享同一存储空间;可以看出StackAllocator::Source采用base::AlignedMemory分配对齐的原始堆栈缓冲区stack_buffer_以及一个保存当前缓冲区是否被使用的标识used_stack_buffer_;
分配器StackAllocator,内部含可重新指定类型的rebind;可共享存储缓冲区的复制构造函数;显式的构造函数;申请空间的allocate,当Source未被其他使用时,可直接分配该缓冲区,否则则直接通过std::allocator<T>::allocate为其分配,同样的deallocate,当Source为被释放的缓冲区时,直接释放(实际上只是简单的used_stack_buffer_ = false),否则认为是通过std::allocator<T>::allocate为其分配的,则通过std::allocator<T>::deallocate,释放该缓冲区。