内存池技术的应用和详细说明

此文内容有误 请慎重阅读 个人认为释放内存块的部分写的有问题 无法实现功能

为了控制CE的串口反复不断的分配内存,出现内存碎片,防止出现内存泄露,于是把从MFC上学到的那个内存池简化了一下,直接用到了程序上

,虽然很简单,但是如果只要稍加二次封装,即可写出类似于MFC中通过哈希表进行内存控制访问的方法。具体的方法呢,直接上代码最清楚:

.h文件
//内存分配结构体
typedef struct __LinkList
{
 struct __LinkList *pNext;
 BYTE buf[2048];
 int len;
}LINKLIST,*PLINKLIST;

int const BUFSIZE = 10;   //分配内存块大小
class CMemoryPool
{
public:
 CMemoryPool(void);
  ~CMemoryPool(void);
public:
 void InitPars(void);
 LINKLIST* GetFreeBuf(void);         //得到空闲内存
 bool AddFreeList(LINKLIST*pLink);   //将用毕内存返回空闲链表
 bool FreeBufRelease(void);          //释放所有内存池内内存
public:
 LINKLIST* m_pLinklistHeader;        //分配内存单位的头
 LINKLIST* m_pLinkListFreeHeader;    //空闲链表内存BUF头
 int m_iPoolFreeCount;    //缓冲区内单元大小数量
 LINKLIST *m_pllink;                 //内存缓冲区分配指针
};
.cpp文件

#include "StdAfx.h"
#include "MemoryPool.h"

CMemoryPool::CMemoryPool(void)
: m_pLinklistHeader(NULL)
, m_pLinkListFreeHeader(NULL)
, m_iPoolFreeCount(0)
, m_pllink(NULL)
{
 InitPars();//初始化内存池
}

CMemoryPool::~CMemoryPool(void)
{
 FreeBufRelease();
}


void CMemoryPool::InitPars(void)
{
 //分配内存
 this->m_iPoolFreeCount = BUFSIZE;
 m_pllink = (LINKLIST*)new BYTE[sizeof(LINKLIST) * BUFSIZE];
 memset(m_pllink,0,sizeof(LINKLIST) * BUFSIZE);
 m_pllink += BUFSIZE - 1;
 this->m_pLinklistHeader = m_pllink; //指定空闲结点

 for (int num = BUFSIZE; num >= 0;num--,m_pllink--)
 {
  m_pllink->pNext = this->m_pLinkListFreeHeader;
  this->m_pLinkListFreeHeader = m_pllink;
 }
}


LINKLIST* CMemoryPool::GetFreeBuf(void)
{
 //取得内存地址
 LINKLIST* pLink = this->m_pLinkListFreeHeader;
 this->m_pLinkListFreeHeader = this->m_pLinkListFreeHeader->pNext; //摘走一个内存单元
 this->m_iPoolFreeCount--;

 if (m_iPoolFreeCount <= 0)
 {
  AfxMessageBox(_T("内存池耗尽!"));
  return NULL;
 }
 return pLink;
}


bool CMemoryPool::AddFreeList(LINKLIST*pLink)
{
 //挂到最后一个结点
 this->m_pLinklistHeader->pNext = pLink;
 pLink->pNext = NULL;
 this->m_pLinklistHeader = pLink;
 this->m_iPoolFreeCount++;

 return true;
}

 

bool CMemoryPool::FreeBufRelease(void)
{
 delete []m_pllink;
 return false;
}

这里有一个小插曲,我在定义LINKLIST这个结构体时最初struct __LinkList *pNext;在最后,结果一使用就报字节对齐的问题,按说指针也应该是一个整形数据啊,后来把它移到结构体的头上,把INT LEN移到最后,就不报了,没找到具体别的原因。

为了更容易的让大家使用和理解,我把MFC的内存池原理代码也附上,如果你想弄得更清楚,可以看王艳平老师的WINDOWS程序设计第五章,这

里向大家建一言,一定要多看书,而且一定要看好书,更要会看书,取其精华,舍其糟粕,又扯远了。

///
// _AFXPLEX_.H文件

#ifndef __AFXPLEX_H__
#define __AFXPLEX_H__

#include "_afxwin.h"

struct CPlex
{
 CPlex* pNext; // 向每个内层块中添加的额外信息,指向下一个内存块首地址的指针

 // 这里是真正的数据区,BYTE data[maxNum*elementSize];
 void* data() { return this + 1; }

 // 用于申请内存的全局函数。申请cbElement大小的空间nMax个
 static CPlex* Create(CPlex*& pHead, UINT nMax, UINT cbElement);

 // 释放以当前对象为首地址(this指针)的内存链中的所有内存
 void FreeDataChain();
};

#endif // __AFXPLEX_H__

#include "_afxplex_.h"

CPlex* CPlex::Create(CPlex*& pHead, UINT nMax, UINT cbElement)
{
 CPlex* p = (CPlex*)new BYTE[sizeof(CPlex) + nMax*cbElement];

 // 将新增加的内存块添加到链中,并将其地址做为首地址
 p->pNext = pHead;
 pHead = p;  // 以相反方向添加数据项的方式大大减化了程序设计
 return p;
}

void CPlex::FreeDataChain()
{
 // 以当前内存块的地址为首地址
 CPlex* p = this;
 // 释放链中所有内存块占用的内存
 while(p != NULL)
 {
  BYTE* pBytes = (BYTE*)p;
  CPlex* pNext = p->pNext;
  delete[] pBytes;
  p = pNext;
 }
}
注意void* data() { return this + 1; }这个技巧,呵呵,新学的可能不太明白,这里说一下,在函数CPlex* p = (CPlex*)new BYTE

[sizeof(CPlex) + nMax*cbElement];里面,我们分配了一大块内存区域,包括一块CPlex大小的内存+nMax*cbElement实际需要的大小内存,这样,而这一整块的类型被强制转换成了CPlex*,我们知道,指针++,会跳跃一个指针指向内容的大小的单元,举个例子,如果CHAR*P,P++会移

动一个字符,如果是INT*P,P++会移动四个字符,如此类推。所以,你明白了吧。
还有就是CPlex::FreeDataChain函数里的CPlex* p = this;然后强制转一下成BYTE*,直接释放,内存管理起来多么方便,就不用再单独的释放数据区的内存了。(可以同我的博文“结构体最后的长度为0或1数组的作用”印证来看。)

大家注意,这个内存块是逆向增加的,所以你分配的最后一个内存块恰恰是你的

this对象,明白了吧。

///
// _AFXCOLL.H文件

#ifndef __AFXCOLL_H__
#define __AFXCOLL_H__

#include "_afxwin.h"
#include "_afxplex_.h"

class CMapPtrToPtr
{
protected:
 // 关联(Association)
 struct CAssoc
 {
  CAssoc* pNext; // 指向下一个CAssoc结构
  void* key; // 键对象
  void* value; // 值对象
 };

public:
// 构造函数(Construction)
 CMapPtrToPtr(int nBlockSize = 10);

// 属性成员(Attributes)
 // 元素的个数
 int GetCount() const;
 BOOL IsEmpty() const;
 // 在映射中查找key所对应的rValue
 BOOL Lookup(void* key, void*& rValue);

// 操作(Operations)
 // 查找或者是添加key对应的value
 void*& operator[](void* key);
 // 添加一个新的(key, vaule)对
 void SetAt(void* key, void* newValue);

 // 移除一个存在的(key, ?)对
 BOOL RemoveKey(void* key);
 void RemoveAll();

 UINT GetHashTableSize() const;
 void InitHashTable(UINT nHashSize, BOOL bAllocNow = TRUE);
 UINT HashKey(void* key) const;

// 实现(Implementation)
protected:
 CAssoc** m_pHashTable;
 int m_nHashTableSize;

 struct CPlex* m_pBlocks; // 保存用CPlex::Create函数申请的内存块的首地址
 int m_nBlockSize;  // 指定每个内存块可以容纳多少个CAssoc结构
 CAssoc* m_pFreeList;  // 预留空间中没有被使用的CAssoc结构组成的链中第一个关联的指针
 int m_nCount;   // 记录了程序一共使用了多少个CAssoc结构,即关联的个数

 // 为一个新的CAssoc结构提供空间,相当于使用new操作符
 CAssoc* NewAssoc();
 // 释放一个CAssoc结构占用的空间,相当于使用delete操作符
 void FreeAssoc(CAssoc* pAssoc);
 // 寻找键key所在的关联
 CAssoc* GetAssocAt(void* key, UINT& nHash) const;
public:
 ~CMapPtrToPtr();
};

inline int CMapPtrToPtr::GetCount() const
{ return m_nCount; }
inline BOOL CMapPtrToPtr::IsEmpty() const
{ return m_nCount == 0; }
inline void CMapPtrToPtr::SetAt(void* key, void* newValue)
{ (*this)[key] = newValue; }
inline UINT CMapPtrToPtr::GetHashTableSize() const
{ return m_nHashTableSize; } 

#endif // __AFXCOLL_H__

///
// MAP_PP.CPP文件


#include "_afxcoll.h"

CMapPtrToPtr::CMapPtrToPtr(int nBlockSize)
{
 m_pHashTable = NULL;
 m_nHashTableSize = 17; // 默认大小
 m_pBlocks = NULL;
 m_nBlockSize = nBlockSize;
 m_pFreeList = NULL;
 m_nCount = 0;
}

inline UINT CMapPtrToPtr::HashKey(void* key) const
{
 UINT Value = ((UINT)(void*)(DWORD)key) >> 4;
 return Value;
}

void CMapPtrToPtr::InitHashTable(UINT nHashSize, BOOL bAllocNow)
{
 if(m_pHashTable != NULL)
 {
  // 释放计算表
  delete[] m_pHashTable;
  m_pHashTable = NULL;
 }

 if(bAllocNow)
 {
  // 为计算表申请空间
  m_pHashTable = new CAssoc*[nHashSize];
  memset(m_pHashTable, 0, sizeof(CAssoc*)*nHashSize);
 }
 m_nHashTableSize = nHashSize;
}

CMapPtrToPtr::CAssoc* CMapPtrToPtr::GetAssocAt(void* key, UINT& nHash) const
{
 // 计算包含key的项在表中的位置
 nHash = HashKey(key)%m_nHashTableSize;
 if(m_pHashTable == NULL)
  return NULL;

 // 在以m_pHashTable[nHash]为头指针的链表中查找
 CAssoc* pAssoc;
 for(pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext)
 {
  if(pAssoc->key == key)
   return pAssoc;
 }
 return NULL;
}

BOOL CMapPtrToPtr::Lookup(void* key, void*& rValue)
{
 UINT nHash;
 CAssoc* pAssoc = GetAssocAt(key, nHash);
 if(pAssoc == NULL)
  return FALSE; // 没有在映射中

 rValue = pAssoc->value;
 return TRUE;
}

void*& CMapPtrToPtr::operator [] (void* key)
{
 UINT nHash;
 CAssoc* pAssoc;
 if((pAssoc = GetAssocAt(key, nHash)) == NULL)
 {
  if(m_pHashTable == NULL)
   InitHashTable(m_nHashTableSize);

  // 既然映射中没有用户指定的项,我们就添加一个新的关联
  pAssoc = NewAssoc();
  pAssoc->key = key;

  // 将新的关联放入计算表中(放在表头,不是表尾)
  pAssoc->pNext = m_pHashTable[nHash];
  m_pHashTable[nHash] = pAssoc;
 }
 return pAssoc->value; // 返回value的引用
}

BOOL CMapPtrToPtr::RemoveKey(void* key)
{
 if(m_pHashTable == NULL) 
  return FALSE; // 表中什么也没有

 CAssoc** ppAssocPre; // 记录要删除的项的地址的变量的地址(如果存在的话,我们会改变此地址处的值)
 ppAssocPre = &m_pHashTable[HashKey(key)%m_nHashTableSize];

 CAssoc* pAssoc;
 for(pAssoc = *ppAssocPre; pAssoc != NULL; pAssoc = pAssoc->pNext)
 {
  if(pAssoc->key == key)
  {
   // 移除pAssoc指向的项
   *ppAssocPre = pAssoc->pNext;  // 从表中移除
   FreeAssoc(pAssoc);       // 释放内存
   return TRUE;
  }
  ppAssocPre = &pAssoc->pNext;
 }
 return FALSE; // 没有找到
}

CMapPtrToPtr::~CMapPtrToPtr()
{
 RemoveAll();
}

void CMapPtrToPtr::RemoveAll()
{
 if(m_pHashTable != NULL)
 {
  // 释放计算表
  delete[] m_pHashTable;
  m_pHashTable = NULL;
 }

 m_nCount = 0;
 m_pFreeList = NULL;
 m_pBlocks->FreeDataChain();
 m_pBlocks = NULL;
}

CMapPtrToPtr::CAssoc* CMapPtrToPtr::NewAssoc()
{
 // 预留的空间已经被使用完
 if(m_pFreeList == NULL)
 {
  // 向m_pBlocks指向的链中添加一个新的内存块。m_nBlockSize的值可以由用户指定
  CPlex* newBlock = CPlex::Create(m_pBlocks, m_nBlockSize, sizeof(CAssoc));

  // 将新内存块中各CAssoc结构添加到m_pFreeList指向的链中(空闲链)
  CAssoc* pAssoc = (CAssoc*)newBlock->data();
  pAssoc += m_nBlockSize -1; // 注意,此时pAssoc指向内存块中最后一个CAssoc结构 
  for(int i = m_nBlockSize -1; i >= 0; i--, pAssoc--)
  {
   // 将pAssoc做为链的首地址添加到空闲链中(还是以相反的顺序向链中添加元素)
   pAssoc->pNext = m_pFreeList; 
   m_pFreeList = pAssoc;
  }
 }

 // 从空闲链中取出一个元素pAssoc
 CAssoc* pAssoc = m_pFreeList;
 m_pFreeList = m_pFreeList->pNext; 
 m_nCount++;    // 又多使用了一个CAssoc结构

 // 初始化新关联的值
 pAssoc->key = 0;
 pAssoc->value = 0;
 return pAssoc;
}

void CMapPtrToPtr::FreeAssoc(CAssoc* pAssoc)
{
 // 将要释放的关联做为链的首地址添加到空闲链中(以相反的顺序)
 pAssoc->pNext = m_pFreeList;
 m_pFreeList = pAssoc;
 m_nCount--;    // 释放了一个CAssoc结构

 // 如果全部的关联都没被使用,就释放所有的内存空间,包括CPlex::Create函数申请的内存块
 if(m_nCount == 0)
  RemoveAll();  // 此函数会释放所有内存空间,待会儿我们再讨论
}

 

这样看,大家就都明白了,我的那个整好是把外面封包的部分给砍掉了。这个同样适合于在WINDOWS平台下

努力!!!!!!!!!!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值