Lexer的设计--中(4)
設(shè)計(jì)一個(gè)小型的內(nèi)存池以及鏈表
上一節(jié)擼到萬(wàn)事俱備只欠真正的lex, 但是lex的作用是將源代碼轉(zhuǎn)化為Token流, 用什么保存Token? 這就涉及到我們要接觸的第一個(gè)數(shù)據(jù)結(jié)構(gòu)—鏈表, 雖然標(biāo)準(zhǔn)庫(kù)中很多容器都可以承擔(dān)鏈表的任務(wù), 但是我說(shuō)過(guò)出于鍛煉原因, 我會(huì)盡量不使用stl中的容器, 所以我決定自己擼一個(gè)鏈表出來(lái), 既然之后大多數(shù)的容器都要自己擼, 干脆連內(nèi)存池也一并擼一個(gè)出來(lái), 所以這一節(jié)的兩個(gè)目的就是 : 內(nèi)存池以及基于這個(gè)內(nèi)存池的鏈表.
我個(gè)人接觸內(nèi)存池的時(shí)間不長(zhǎng), 準(zhǔn)確的說(shuō)我目前腦袋里面只有兩中內(nèi)存池的思路, 一種是sgi stl內(nèi)存池,我之前有一篇文章專門對(duì)這種內(nèi)存池做過(guò)講解, 但是這里我會(huì)使用另外一種較為簡(jiǎn)易的內(nèi)存池模型, 因?yàn)槲艺J(rèn)為sgi stl準(zhǔn)確來(lái)說(shuō)可以支持頻繁地不同大小的內(nèi)存分配, 而我這里會(huì)使用一種簡(jiǎn)化的思路, 使之每一個(gè)內(nèi)存池只支持單一大小的內(nèi)存分配. 這種內(nèi)存池也是從別人那里學(xué)過(guò)來(lái)的, 簡(jiǎn)圖差不多是這樣.
然后源代碼差不多是這樣...
#ifndef FRED_MEMORYPOOL_H #define FRED_MEMORYPOOL_H#include <cstdio> #include <cstdlib> #include <vector>template <typename T, size_t NumberForOneNode = 32> class MemoryPool{ private:struct node{void* space;node* next;};node *head, *tail;size_t left;void* cur;protected:MemoryPool():left(NumberForOneNode){tail = head = new node;head->next = 0;cur = head->space = static_cast<T*>(malloc(sizeof(T) * NumberForOneNode));}//Big threeMemoryPool(const MemoryPool&) = delete;MemoryPool& operator=(const MemoryPool& rhs) = delete;~MemoryPool();void* allocate(); };template <typename T, size_t NumberForOneNode> MemoryPool<T, NumberForOneNode>::~MemoryPool() {while(true) {if (head == tail) {free(head->space);delete head;return;}auto temp = head;head = head->next;free(temp->space);delete temp;} }template <typename T, size_t NumberForOneNode> void* MemoryPool<T, NumberForOneNode>::allocate() {if(left--){auto re = cur;cur = reinterpret_cast<char*>(cur) +sizeof(T);return re;}left = NumberForOneNode;auto newNode = new node;newNode->next = 0;tail = tail->next = newNode;cur = newNode->space = static_cast<T*>(malloc(sizeof(T) * NumberForOneNode));allocate(); }#endif //FRED_MEMORYPOOL_H圖上的變量和代碼里面的變量名字都是統(tǒng)一的, 很好理解...
這個(gè)內(nèi)存池的最后一步, 是在這個(gè)內(nèi)存池的基礎(chǔ)上, 再套一層封裝.
#ifndef FRED_ALLOCATOR_H #define FRED_ALLOCATOR_H#include "MemoryPool.h"template <typename T, size_t NumberForOneNode = 32> class Allocator : private MemoryPool<T, NumberForOneNode> { private:void* buffer[NumberForOneNode];size_t left;public:Allocator():left(0){};void* allocator(){if(left){return buffer[--left];}else{return MemoryPool<T, NumberForOneNode>::allocate();}}void deallocator(T* ptr){ptr->~T();if(left == NumberForOneNode){//fullreturn;} buffer[left++] = ptr;} };#endif //FRED_ALLOCATOR_H思想其實(shí)很簡(jiǎn)單, 就是如果有空間被送回, 并不直接交還給系統(tǒng), 而是用這個(gè)叫做buffer的數(shù)組存著, 如果之后再有需要, 優(yōu)先從數(shù)組中取, 其實(shí)這里用vector要比buffer更好, 但是如果想要重利用的空間規(guī)模不大的話, buffer也夠用, 就算這個(gè)buffer也不會(huì)內(nèi)存泄漏, 只是有一些空間被浪費(fèi)了, 等到維護(hù)這個(gè)allocator的類卒了, 空間還是要被釋放的.
如果想看這個(gè)內(nèi)存池的原版本實(shí)現(xiàn), 可以看這里
原版本實(shí)現(xiàn)
有了內(nèi)存池, 根據(jù)我們的需求我們只需要一個(gè)鏈表.
#ifndef FRED_LINKLIST_H #define FRED_LINKLIST_H#include "MemoryPool/Allocator.h"template <typename T> class LinkList{ private:struct node{T item;struct node* next;};Allocator<node> allocator;node* head;node* cur;size_t size;public:LinkList():head(0), cur(0), size(0){}LinkList(const LinkList&) = delete;LinkList& operator=(const LinkList&) = delete;~LinkList(){for(auto temp = head; temp != cur; temp = temp->next){allocator.deallocator(temp);}allocator.deallocator(cur);}void pushBack(const T& item){if(cur) {cur = cur->next = reinterpret_cast<node*>(new(allocator.allocator())T(item));}else {cur = head = reinterpret_cast<node*>(new(allocator.allocator())T(item));}++size;cur->next = 0;}node* getHead() const {return head;} };#endif //FRED_LINKLIST_H對(duì)于這個(gè)鏈表功能和實(shí)現(xiàn)都很簡(jiǎn)單, 這里唯一要說(shuō)可能有些人不知道new可以指定空間進(jìn)行初始化, 不知道的可以去網(wǎng)上看一下, 這是placement new而我們一般使用的帶有內(nèi)存分配的叫做plain new...
大概就是這么多, 這個(gè)禮拜在成都玩, 可能更新地比較慢...
轉(zhuǎn)載于:https://www.cnblogs.com/nzhl/p/5767851.html
總結(jié)
以上是生活随笔為你收集整理的Lexer的设计--中(4)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 自己写的一个复杂查询
- 下一篇: 基础4