此文内容有误 请慎重阅读 个人认为释放内存块的部分写的有问题 无法实现功能
为了控制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 da
void* da
// 用于申请内存的全局函数。申请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* da
[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->da
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平台下
努力!!!!!!!!!!!!!