所谓固定长度内存池是指每次从内存池申请的内存长度是不能变的,如果内存池初始化每块内存的大小是100字节,那么每次申请出来的内存长度就是100字节,不会多也不会少。
固定长度的内存池有很多的应用场景,比如图像的编解码,码率的固定导致每帧图像的大小是比较固定的,因此可以使用固定长度的内存池来优化图像缓存的申请和释放。
隐式链表是一个单向的链表,使用每块空闲内存块的前四个字节来保存下一块空闲内存块的地址
完整代码如下:
class CFixSizeMemPool
{
public:
//构造函数申请内存
CFixSizeMemPool(unsigned int iBlockSize, unsigned int iBlockCount)
{
//由于需要用块内存构造空闲链表,因此块长度必须大于一个地址的长度
if (iBlockSize < sizeof(void*))
{
iBlockSize = sizeof(void*);
}
m_iBlockSize = iBlockSize;
m_iBlockCount = iBlockCount;
//向操作系统申请整块内存
m_pcMemPool = (char*)::malloc(iBlockSize * iBlockCount);
if (m_pcMemPool == 0)
{
throw std::exception("no memory.");
}
//初始化空闲块链表
buildFreeLink();
}
//析构函数释放整块内存
~CFixSizeMemPool()
{
::free(m_pcMemPool);
m_pcMemPool = 0;
}
//申请一块内存
void* malloc(unsigned int iLen)
{
//申请的内存长度不能大于块的长度
if ((iLen > m_iBlockSize) || (m_pcFreeBlock == 0))
{
return 0;
}
//分配出空闲块链表头上内存块,同时链表头指向下一空闲块
char *pcBlock = m_pcFreeBlock;
m_pcFreeBlock = *((char **)pcBlock);
return pcBlock;
}
//释放一块内存
void free(void *pMem)
{
//判断内存地址是否有效
if ((pMem < m_pcMemPool) ||
(pMem > (m_pcMemPool + (m_iBlockSize * m_iBlockCount))))
{
return;
}
//判断内存是否和块大小是对齐的
if (((char*)pMem - m_pcMemPool) % m_iBlockSize != 0)
{
throw std::exception("invalid address.");
}
//把内存块放到空闲链表头上
*((char **)pMem) = m_pcFreeBlock;
m_pcFreeBlock = (char*)pMem;
}
private:
//初始化空闲内存块队列
void buildFreeLink()
{
char **pcNextFreePtr = 0;
for (unsigned int i=0; i<m_iBlockCount; ++i)
{
pcNextFreePtr = (char**)(m_pcMemPool + (i * m_iBlockSize));
*pcNextFreePtr = m_pcMemPool + (i+1) * m_iBlockSize;
}
*pcNextFreePtr = 0;
m_pcFreeBlock = m_pcMemPool;
}
unsigned int m_iBlockSize; //每块的大小
unsigned int m_iBlockCount; //块的数量
char *m_pcFreeBlock; //指向空闲块链表的头
char *m_pcMemPool; //指向整块内存的地址
};