【C++】内存池的实现

一、内存池概念

内存池是一种自主的内存管理机制。就是将我们的内存管理放在了应用程序端。
那么它的简单处理做了什么事呢?
首先,我们从堆上分配出一块很大的内存块。接着我们按照划分将其划分成每个不同的小组。这个每个小组存储一个数据块。针对于每个小组的组内来说就是一个简单的数据结构。这个数据结构我们将其分为两个部分,这两部分代表分别存储数据的数据域找到下一个指针的指针域。
划分好之后我们用一个标志来标志它的起始位置。最后形成一个静态链表的组织方式,将第一个内存单元的指针域指向第二个内存单元。第二个内存单元指向下一个内存单元。如下图所示:
在这里插入图片描述
现在这个内存块管理的是未分配的内存单元。

二、通用内存池的实现

这个设计是将内存池和使用类单独分开来设计。我们是先设计一个内存池实现的类,然后再设计使用类。这个使用类的设计我们是这样的:如果这个使用类中有new,那么如果它通过operator new来处理的时候可以在operator new里面实现自主的内存管理。
那么我们实现的时候使用类是如何在内存池上开辟内存的呢?
这样的话这两个类就应该产生一个叫信息交互的类。也就是这两个类应该进行消息通讯。那么在C++中最简单的消息通讯就是产生接口。那么另一个类只要调用它的接口完成这个行为就可以了。所以在通用内存池的实现中我们只要让使用类去调用内存池的通用接口就可以了。
那么内存池应该提供哪些接口呢?
我们知道内存池的本质管理的是堆内存。那么内存的管理的第一件事就是alloc()开辟内存。第二件事就是dealloc()回收内存。
所以内存池对外就提供两个接口:
1、alloc();申请内存
2、dealloc();释放内存
如下图所示:
在这里插入图片描述

三、代码实现通用内存池

const int MEM_SIZE = 10;
template<typename T>
class MEM_POOL//内存池类
{
public:
	MEM_POOL()
	{
		pool = NULL;
	}
	void* alloc(size_t size)
	{
		if(pool == NULL)
		{
			pool = (Node *)new char[(size + 4)* MEM_SIZE]();
			Node* pCur = pool;
			for(pCur; pCur < pool + MEM_SIZE - 1; pCur = ptr)
			{
				pCur->next = pCur + 1;	
			}
			pCur->next = NULL;
		}
		void* rt = pool;
		pool = pool->next;
		return rt;
	}
	void* dealloc(void* ptr)
	{
		Node* nptr = (Node*)ptr;
		if(ptr == NULL)
		{
			return; 
		}
		nptr->pnext = pool;
		pool = nptr;
	}
private:
	struct Node
	{
	public:
		Node(T value = T())
			:val(value), pnext(NULL)
			{}
	public:
		T val;
		Node* pnext;
	};
	Node* pool;
};

class Student
{
public:
Student(std::string name, std::string id, int age)
	:mname(name), mid(id), mage(age)
	{}
	void* operator new(size_t size)
	{
		return mm.alloc(size);
	}
	void operator delete(void* ptr)
	{
		mm.dealloc(ptr);
	}
private:
	std::string mname;
	std::string mid;
	int mage;
	static MEM_POOL<Student> mm;
};
MEM_POOL<Student> Student::mm;

int main()
{
	Student* pstu1 = new Student("zhangsan", "001",20);
	Student* pstu1 = new Student("lisi", "002",19);
	delete pstu1;
	delete pstu2;
	return 0;
}

四、通用内存池的优化—单例模式实现

const int MEM_SIZE = 10;
template<typename T>
class MEM_POOL//内存池类
{
public:
	static MEM_POOL<T>* getInstance()
	{
		if(ppm = NULL)
		{
			ppm = new MEM_POOL<T>();
		}
		return ppm;
	}
	void* alloc(size_t size)
	{
		if(pool == NULL)
		{
			pool = (Node *)new char[(size + 4)* MEM_SIZE]();
			Node* pCur = pool;
			for(pCur; pCur < pool + MEM_SIZE - 1; pCur = ptr)
			{
				pCur->next = pCur + 1;	
			}
			pCur->next = NULL;
		}
		void* rt = pool;
		pool = pool->next;
		return rt;
	}
	void* dealloc(void* ptr)
	{
		Node* nptr = (Node*)ptr;
		if(ptr == NULL)
		{
			return; 
		}
		nptr->pnext = pool;
		pool = nptr;
	}
private:
    MEM_POOL()
	{
		pool = NULL;
	}
	MEM_POOL(const MEM_POOL<T>&);
	struct Node
	{
	public:
		Node(T value = T())
			:val(value), pnext(NULL)
			{}
	public:
		T val;
		Node* pnext;
	};
	Node* pool;
	static MEM_POOL<T>* ppm;
};
template<typename T>
MEM_POOL<T>* MEM_POOL<T>::ppm = NULL;

class Student
{
public:
Student(std::string name, std::string id, int age)
	:mname(name), mid(id), mage(age)
	{}
	void* operator new(size_t size)
	{
		return pmm->alloc(size);
	}
	void operator delete(void* ptr)
	{
		pmm->dealloc(ptr);
	}
private:
	std::string mname;
	std::string mid;
	int mage;
	static MEM_POOL<Student> pmm;
};
MEM_POOL<Student>* Student::pmm = MEM_POOL<Student>::getInstance();

int main()
{
	Student* pstu1 = new Student("zhangsan", "001",20);
	Student* pstu1 = new Student("lisi", "002",19);
	delete pstu1;
	delete pstu2;
	return 0;
}

五、内存池的优点

C/C++下分配足够的内存、追踪内存的分配、在不需要的时候释放内存—这是相当复杂。而直接使用系统调用malloc/free、new/delete进行内存分配和释放,有以下弊端:

  • 调用malloc/new,系统需要根据“最先匹配”、“最优匹配”或其他算法在内存空闲块表中查找一块空闲内存,调用free/delete,系统可能需要合并空闲内存块,这些会产生额外开销
  • 频繁使用时会产生大量内存碎片,从而降低程序运行效率
  • 容易造成内存泄漏
  • 内存池是代替直接调用malloc/free、new/delete进行内存管理的常用方法,当我们申请内存空间时,首先到我们的内存池中查找合适的内存块,而不是直接向操作系统申请,优点在于:
  • 比malloc/free进行内存申请/释放的方式快
  • 不会产生或很少产生堆碎片
  • 可避免内存泄漏
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值