內存池就這麼簡單

內存池的成員變量

剛開始分配的時候,內存池裏面只有一個內存塊,這塊內存塊總共擁有unit_count個單元
如果這塊內存塊不夠用,那麼就要再搞一個新的內存塊,新增的這塊內存塊裏一共有grow_count個單元。
新增的這個內存塊要和原來的內存塊用鏈表連起來。相當於在鏈表的頭部插入。memory_block指針就指向了鏈表裏的第一塊內存塊。
unit_size表示了一個單元擁有幾個字節

class MemoryPool {
public:
	int unit_count;
	int grow_count;
	int unit_size;
	MemoryBlock* memory_block;}

在這裏插入圖片描述

內存塊的成員變量

nsize表示這個內存塊裏有多少個字節。就是單元數*每個單元的字節數得到的。
nfree表示這個內存塊裏有幾個單元是空閒的。
nfirst是第一個空閒的單元,它的編號。編號就是這個內存塊裏第幾個單元。
pNext指針指向下一個內存塊。
address的設計非常巧妙。當申請一塊內存塊的時候,是申請了頭部+身體這麼多的內存。
頭部裏就包含了這個對象的成員變量,大小是sizeof(MemoryBlock)這個類的大小。
身體開始纔是真正存放數據的地方。身體的大小就是nsize咯。
脖子的地方就是身體開始地方。address就是這個脖子,我們要用的不是address這個地址裏存放的東西(*address),而是address這個地址。可以直接通過address來獲取哦。

class MemoryBlock {
public:
	int nsize;
	int nfree;
	int nfirst;
	MemoryBlock* pNext;
	char address[1];}

每個單元的前兩個字節不簡單哦

每個單元的前兩個字節都存放了下一個空閒單元的編號
所以內存塊初始化的時候,要讓這兩個字節等於當前編號+1。
下面是內存塊初始化的代碼

MemoryBlock(int unitcount,int unitsize) {
		nsize = unitcount * unitsize;
		nfree = unitcount;
		nfirst = 0;
		pNext = NULL;
		char *p = address;
		for (int i = 0; i < unitcount - 1; i++) {
			*(short*)p = i + 1;
			p += unitsize;
		}
		*(short*)p = -1;	
	}

在這裏插入圖片描述

向內存池申請一個單元的內存

如果內存池裏面還沒有內存塊,那肯定是先分配內存,調用內存塊的構造函數,先搞一塊內存塊咯。

if (memory_block == NULL) {
			memory_block = (MemoryBlock*)new(unit_count*(sizeof(MemoryBlock) + unit_size))MemoryBlock(unit_count, unit_size);
			assert(memory_block != NULL);
		}

有了內存塊以後檢查這個內存塊還有沒有空閒的單元可以使用的,沒有的話去下一塊內存塊看看。

MemoryBlock *p = memory_block;
		while (p != NULL and p->nfree == 0) {
			p = p->pNext;
		}

找了半天都沒找到空閒的怎麼辦呢?只能再申請一塊內存塊了,還要把它插到內存塊鏈表的頭部。這裏用頭插法

if (p == NULL) {
			p = (MemoryBlock*)new(grow_count*(sizeof(MemoryBlock) + unit_size))MemoryBlock(grow_count, unit_size);
			assert(p != NULL);
			p->pNext = memory_block;
			memory_block = p;
		}

OK,現在找到這個內存塊了。
內存塊裏的nfirst記錄了第一塊空閒的單元。
我們直接返回它不就行了。
可是這樣的話,下次再想從這塊內存塊裏面搞一個單元怎麼辦呢?
所以還是要做一下善後的工作,把nfirst指向下一塊空閒的單元。
並且這個內存塊的空閒單元數減一。

char* free_address=p->address + p->nfirst*p->nsize;
		p->nfirst=*(short*)free_address;
		p->nfree--;
		return free_address;

釋放一個單元的內存

既然你要釋放,你首先要把你要釋放的這個指針傳給我吧。
我直接釋放行不行呢?
肯定不行咯。
這裏的釋放不是說讓你真的把這個內存單元給free掉。而是把它搞成空閒的狀態。
所以首先找到這個單元所在的內存塊

MemoryBlock* pb = memory_block;
		while (pb != NULL and p < pb->address||p>pb->address+pb->nsize) {
			pb = pb->pNext;
		}

要是沒找到的話,這個單元就不是內存池分配的。
找到了以後呢。
算出這個單元在這個內存塊裏編號n.
然後還是頭插法
把這個單元插到這塊內存塊第一個空閒單元的前面。
具體而言,就是讓這個單元的前兩個字節存放第一個空閒單元的編號。
然後讓第一個空閒單元的編號變成這個單元的編號。
空閒單元的個數也可以加上去了。

if (pb != NULL) {
			int n = (int)((char*)p - pb->address) / unit_size;
			*(short*)p = pb->nfirst;
			pb->nfirst = n;
			pb->nfree++;
		}

測試

MemoryPool pool(3, 3, 4);
	int *p1=(int*)pool.Alloc();
	*p1 = 111;
	pool.Free(p1);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章