内存分配也是要耗费时间的。因此,频繁的内存分配会浪费很多时间,我们可以使用内存池来解决这个问题。
参考这篇文章:内存池介绍与经典内存池的实现-腾讯云开发者社区-腾讯云
思路如下图:
如上图所示:浅蓝色代表申请的内存块,蓝色代表内存结点,一个内存块包含有3个内存结点,内存结点由链表管理,如果分配出去则将其从链表中删除。如果释放将其添加回链表的头部,以待重新分配。
3.经典内存池的数据结构设计
内存池模板类应该是如下设计:
两个常量:
内存块大小:MemBlockSize;
结点大小:ItemSize;
两个指针变量:
内存块链表头指针:pMemBlockHeader;
空闲内存结头链表头指针:pFreeNodeHeader;
空闲结点结构体:
// 内存结点
struct FreeNode
{
// 指向下一个内存结点的指针
FreeNode* pNodeNext;
// 数据对象
char data[ObjectSize];
};
内存块结构体:
// 内存块
struct MemBlock
{
// 指向下一个内存块
MemBlock* pMemNext;
// 一个内存块中内存结点的数目
FreeNode data[NumofObjects];
};
经典内存池的实现如下:
MemPool.h文件
#pragma once
template<int ObjectSize,int NumofObjects = 20>
class MemPool
{
private:
// 内存块的大小
const int m_nMemBlocksSize;
// 节点的大小
const int m_nItemSize;
// 内存结点
struct FreeNode
{
// 指向下一个内存结点的指针
FreeNode* pNodeNext;
// 数据对象
char data[ObjectSize];
};
// 内存块
struct MemBlock
{
// 指向下一个内存块
MemBlock* pMemNext;
// 一个内存块中内存结点的数目
FreeNode data[NumofObjects];
};
// 链表中指向内存头结点链表的指针
FreeNode* m_pFreeNodeHeader;
// 链表中指向内存块头结点的指针
MemBlock* m_pMemBlockHeader;
public:
// 构造函数
MemPool() :m_nItemSize(ObjectSize + sizeof(FreeNode*)),
m_nMemBlocksSize(sizeof(MemBlock*) + NumofObjects * (ObjectSize + sizeof(FreeNode*)))
{
m_pFreeNodeHeader = nullptr;
m_pMemBlockHeader = nullptr;
}
// 析构函数
~MemPool()
{
MemBlock* ptr;
while (m_pMemBlockHeader)
{
ptr = m_pMemBlockHeader->pMemNext;
delete m_pMemBlockHeader;
m_pMemBlockHeader = ptr;
}
}
/// <summary>
/// Malloc: 分配内存
/// </summary>
/// <returns></returns>
void* Malloc();
/// <summary>
/// Free:释放内存
/// </summary>
/// <param name=""></param>
void Free(void*);
};
// 分配内存
template<int ObjectSize, int NumofObjects>
void* MemPool<ObjectSize,NumofObjects>::Malloc()
{
//
if (m_pFreeNodeHeader == nullptr)
{
// 申请内存块
MemBlock* pNewBlock = new MemBlock();
// 首先将第下一个内存结点的设置为nullptr
pNewBlock->data[0].pNodeNext = nullptr;
for (int i=1;i< NumofObjects;++i)
{
// 如果申请内存数超过一个结点
pNewBlock->data[i].pNodeNext = &pNewBlock->data[i - 1];
}
// 将内存头结点指针指向申请内存数目的后一位结点
m_pFreeNodeHeader = &pNewBlock->data[NumofObjects - 1];
//
pNewBlock->pMemNext = m_pMemBlockHeader;
}
void* freeNode = m_pFreeNodeHeader;
// 指针头结点指向下一个内存结点
m_pFreeNodeHeader = m_pFreeNodeHeader->pNodeNext;
return freeNode;
}
// 释放内存
template<int ObjectSize, int NumofObjects>
void MemPool<ObjectSize, NumofObjects>::Free(void* p)
{
FreeNode* pNode = (FreeNode*)p;
// 结点链表中的操作,当前要释放结点时,先指向当前头结点
pNode->pNodeNext = m_pFreeNodeHeader;
// 最后将头结点当前节点指向当前节点
m_pFreeNodeHeader = pNode;
}
MemPool.cpp文件
#include <iostream>
#include"MemPool.h"
using namespace std;
class ActualClass
{
public:
ActualClass()
{
m_nNO = m_nCount;
m_nCount++;
}
void Print()
{
cout << this << ":";
cout << "the " << m_nNO << " Object" << endl;
}
void* operator new(size_t size);
void operator delete(void* p);
private:
static int m_nCount;
int m_nNO;
};
MemPool<sizeof(ActualClass), 2> mp;
void* ActualClass::operator new(size_t size)
{
return mp.Malloc();
}
void ActualClass::operator delete(void* p)
{
mp.Free(p);
}
int ActualClass::m_nCount = 0;
int main()
{
// 定义一个指针对象
ActualClass* p1 = NULL;
p1 = new ActualClass();
p1->Print();
ActualClass* p2 = new ActualClass();
p2->Print();
delete p1;
p1 = new ActualClass();
p1->Print();
ActualClass* p3 = new ActualClass();
p3->Print();
delete p1;
delete p2;
delete p3;
system("pause");
std::cout << "Hello World!\n";
}
上面的程序裕兴结果如下:
说明:如结果所示:在分配完p2时释放p1,然后再次为p1分配内存时,这个新的内存恰好是上一次释放的内存。如果把它p1改成p3,则p3的地址和p1原来的地址相同。上面结果中p3的地址是新分配的内存块中的地址。
欢迎大家一起交流学习.
参考:
内存池介绍与经典内存池的实现