内存池和通用内存池的简单制作

简单内存池

内存池实际上是一种内存的分配模式。它不同于我们平常用的new,malloc来让系统进行内存的分配,它是我们人为的从系统中申请出来一块内存来进行分配。如果我们让系统自动分配则会产生许多内存碎片,这会降低我们的行能,造成内存浪费。
内存碎片又分为外内存碎片和内内存碎片,
其中外存碎片是指:已经分配出去的内存释放后,该内存不能再重新分配。
内碎片是指:开辟过程中,使用点需要的空间小于实际分配的空间所造成的空间浪费。
如下图,我们再使用点我们实际需要的大小是4+1,5个字节,但是系统要字节对齐,所以就会分配出来8个字节的大小供我们使用,那么剩余的三个字节就是内存浪费。
在这里插入图片描述
那我们使用内存池有什么有点呢?首先我们不用频繁的new,malloc来申请空间,使得效率提高。其次我们申请的内存池一般都是一块很大的内存,当我们释放该内存时是及不容易产生内存碎片的,内存资源利用率也会提高。

#include <iostream>
template <typename T>
class queue;
const int max_size = 10;
template <typename T>
class queueitem//队列内存池
{
public:
	queueitem(T val = T())                                 //构造函数。初始化val,使它可以被T类型的任意值赋值
		:mdata(val), pnext(NULL)
	{}
	void* operator new(size_t size)//一种用来记录大小的数据类型(相当于定义了一个无符号整形的size)
	{
		if (pool == NULL)//如果pool指向空
		{
			pool = (queueitem<T>*)new char[size * max_size];//申请size*10个大小的char 类型空间,并把它强转为queueitem<T>*类型,
														    //此时pool指向了新开辟空间的第一个空间
			queueitem<T>* pcur = pool;//定义一个指向pool的指针
			for (pcur; pcur < pool + max_size - 1; pcur = pcur + 1)//pcyr自加往下走
				/*pool+max_size-1,pool指向了最后一个空间,pcur小于它,代表pcur指向了倒数第二个*/
			{
				pcur->pnext = pcur + 1;//从首地址开始,它的pnext域指向了下一个位置,使得空间成为一个链表
			}
			pcur->pnext = NULL;//链表完成后将第一个地址的pnext位置置为空
		}
			queueitem<T>* rt = pool->pnext;
			pool->pnext = rt->pnext;
			return rt;
		//void* ptr = pool;//新申请的空间供外部使用时,需要将第一个节点赋给外界
		//pool = pool->pnext;//将第一个节点往后偏移一下
		//return ptr;//将ptr返回出去
	}
	void operator delete(void* ptr)//删除节点,将节点回收到内存池中
	{

		queueitem<T>* qptr = (queueitem<T>*)ptr;//将要回收的节点赋给qptr  void*没办法直接处理,所以将其转换为queueitem<T>*的类型
		qptr->pnext = pool;//将要回收的指针重新指向pool
		pool = qptr;//将qptr作为首地址,调整pool位置
	}
private:
	template <typename T>
	friend class queue;
	T mdata;
	queueitem<T>* pnext;
	static queueitem<T>* pool;//定义一个静态的变量pool
};
template <typename T>
queueitem<T>* queueitem<T>::pool = NULL;//对pool的声明

template <typename T>
class queue 
{
public:
	queue()
	{
		phead = ptail = new queueitem<T>();//头指针和尾指针都指向同一个节点
	
	}
	~queue()
	{
		queueitem<T>* ptt = phead;
		queueitem<T>* pnext;
		while (ptt != NULL)//如果头指针不为空
		{
			pnext = ptt->pnext;//把后续保留下来
			delete ptt;
			//调用析构函数
			//调用operator delete释放
			ptt = pnext;
		}
		phead = NULL;
	}
	void push(T val)//入队操作
	{
		queueitem<T>* pnewitem = new queueitem<T>(val);//新申请一个节点从内存池中
		ptail->pnext = pnewitem;//尾指针的pnext指向了新申的节点
		ptail = ptail->pnext;//尾指针往后走
	}
	bool empty()//判空
	{
		return (phead == ptail) && (phead != NULL);
	}
	void pop()//出队
	{
		if (empty())
		{
			throw std::exception("queu is empty");
		}
		queueitem<T>* ptr = phead->pnext;//新申请的节点指向了头部指向的下一个位置,第一个数据节点
		phead->pnext = ptr->pnext;//头指针的下一个位置指向了第一个指针的pnext域。相当于头指针和第一个节点同时指向了第二个节点
		delete ptr;//将第一个节点释放
	}
	T back()
	{
		if (empty())
		{
			throw std::exception("queu is empty");
		}
		return ptail->mdata;
	}
	T front()
	{
		if (empty())
		{
			throw std::exception("queu is empty");
		}
		return phead->pnext->mdata;
	}
private:
	queueitem<T>* phead;
	queueitem<T>* ptail;
};
int main()
{
	queue<int> que;
	for (int i = 0; i < 5; i++)
	{
		que.push(i + 1);
	}
	que.pop();
	auto qfront = que.front();
	auto qback = que.back();
	std::cout << "queue.front:" << qfront << std::endl;
	std::cout << "queue.fback:" << qback << std::endl;
	return 0;
}

其中size_t实际上是unisgned int 类型的别名,即无符号整数
根据C标准,rsize_t类型必须是 size_t

通用内存池

虽然我们再上面已经开辟好了一个内存池,但是它的局限性太明显。即它只能再queuepool中申请内存。所以我们就会想开辟一个通用的内存池,不管哪个类来申请时我们都可以分配出去内存供外部使用。
alloc:开辟内存
dealloc:回收内存
我们只需要在申请类中的operator new中调动alloc接口开辟内存

#include<iostream>
#include <string>
const int max_size = 10;
template <typename T>//我们不知道将存放什么类型的数据,所以给出T模板
class mem_pool
{
public:
	mem_pool()
	{
		pool = NULL;//将poll指向NULL
	}
	void* Alloc(rsize_t size)//计算的是student数据域的大小
	{
		if (pool == NULL)
		{
			pool = (node*)new char[(size+4)* max_size];//在内存池中,不但有指针域还有数据域
			node* paa = pool;
			for (paa; paa < pool + max_size - 1; paa += 1)
			{
				paa->pnext = paa + 1;
			}
			paa ->pnext= NULL;//链表完成后最后一个节点的pnext位置置为空
		}
		void* pcc = pool;
		pool = pool->pnext;
		return pcc;
	}
	void Dealloc(void* ptr)
	{
		node* pee = (node*)ptr;//将ptr从(void*)转换为(node*)
		if (pee == NULL)
		{
			return;
		}
		pee->pnext = pool;
		pool = pee;
	}
private:
	friend class student;
	class node
	{
	public:
		node(T val)//针对节点的构造函数
			:mdata(val),pnext(NULL)
		{}
	public:
		T mdata;
		node* pnext;//将数据域和指针域放在一个节点中
	};
	node* pool;//将节点拿出来生成一个静态链表的结构
};
class student
{
public:
	student(std::string name, std::string id, std::string age)
		:mname(name),mid(id),mage(age)
	{}
	void* operator new(size_t size)
	{
		return mm.Alloc(size);//内存池对象通过alloc接口开辟内存
	}
	void operator delete(void* ptr)
	{
		mm.Dealloc(ptr);//内存池对象通过dealloc来回收内存
	}
private:
	std::string mname;
	std::string mid;
	std::string mage;
	static mem_pool<student> mm;//因为是所有的学生共享同一个内存池,所以我们把它设为全局静态变量
};
mem_pool<student> student::mm;//对静态全局变量的申明
int main()
{
	student* pstu1 = new student("zhangsan", "001", "20");
	student* pstu2 = new student("lisi", "002", "21");
	std::cout <<"laile:"  << pstu1 << std::endl;
	std::cout << "laile:" << pstu2 << std::endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值