内存池就这么简单

内存池的成员变量

刚开始分配的时候,内存池里面只有一个内存块,这块内存块总共拥有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);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值