内存对象管理器(基于数组和链表实现)
Posted yzdai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内存对象管理器(基于数组和链表实现)相关的知识,希望对你有一定的参考价值。
1.1 数组的特点
- 连续的内存空间分配并且顺序存储数据,使用之前需要先分配数组个数;
- 可以通过下标进行访问修改数据,时间复杂度为O(1);
- 空间效率不是很好,不能随意修改数组大小;
- 增删数据需要内存拷贝
1.2 链表的特点
- 内存空间分配是分散的,非连续的存储数据;
- 不能通过下标直接访问,查找的时间复杂度为O(n);
- 增删元素,只需要改变前后指针;
1.3 基于数组和指针实现的对象管理器
结合了数组和链表的优点,可以O(1)查找元素,O(1)增删元素;
需要预分配内存;
2.代码实现(C++)
/** *@file ObjAllocator *@author jasonxiong *@date 2009-12-09 *@version 1.0 *@brief CObj对象分配类,即新版本的idxobj * * (1)一般用于大型对象的内存分配 */ #ifndef __OBJ_ALLOCATOR_HPP__ #define __OBJ_ALLOCATOR_HPP__ #include <stdio.h> namespace ServerLib typedef enum enmObjAllocType EOAT_ALLOC_BY_SELF = 0, //!<对象内存由ObjAllocator自已动态分配 EOAT_ALLOC_BY_SHARED_MEMORY = 1, //!<对象内存由共享内存分配 ENMOBJALLOCTYPE; class CObj; typedef enum enmIdxUseFlag EIUF_FREE = 0, //!<该对象未被使用 EIUF_USED = 1, //!<该对象已被使用 ENMIDXUSEFLAG; //!索引类,仅在CObjAllocator中使用,外层一般不用 class CIdx public: CIdx(); ~CIdx(); public: //!初始化函数 int Initialize(); //!将对象设置为未使用 inline void SetFree() m_iUseFlag = EIUF_FREE; //!将对象设置为已使用 inline void SetUsed() m_iUseFlag = EIUF_USED; //!判断对象是否已被使用 inline int IsUsed() const return m_iUseFlag == EIUF_USED; //!获取所在链表下一个索引 inline int GetNextIdx() const return m_iNextIdx; //!设置所在链表下一个索引 inline void SetNextIdx(int iIdx) m_iNextIdx = iIdx; //!获取所在链表上一个索引 inline int GetPrevIdx() const return m_iPrevIdx; //!设置所在链表上一个索引 inline void SetPrevIdx(int iIdx) m_iPrevIdx = iIdx; //!获取指向的对象 inline CObj *GetAttachedObj() const return m_pAttachedObj; //!设置指向的对象 inline void SetAttachedObj(CObj *pObj) m_pAttachedObj = pObj; private: int m_iNextIdx; //!<对象索引块链表指针,指向后一个闲/忙索引 int m_iPrevIdx; //!<对象索引块链表指针,指向前一个闲/忙索引 int m_iUseFlag; //!<该对象是否已经被使用标志 CObj *m_pAttachedObj; //!<所指向的对象指针 ; typedef CObj *(*Function_CreateObj)(void *); class CObjAllocator private: //默认构造函数,不允许上层自行调用 CObjAllocator(); public: CObjAllocator(size_t uiObjSize, int iObjCount, Function_CreateObj pfCreateObj); ~CObjAllocator(); /** *使用共享内存创建ObjAllocator *@param[in] pszKeyFileName 共享内存Attach的文件名 *@param[in] ucKeyPrjID 共享内存的工程ID *@param[in] uiObjSize 对象大小 *@param[in] iObjCount 对象个数 *@param[in] *@return 0 success */ static CObjAllocator *CreateBySharedMemory(const char *pszKeyFileName, const unsigned char ucKeyPrjID, size_t uiObjSize, int iObjCount, Function_CreateObj pfCreateObj); /** *指定内存指针来创建CObjAllocator *@param[in] pszMemoryAddress 指定的内存 *@param[in] uiMemorySize 内存大小 *@param[in] uiObjSize 对象大小 *@param[in] iObjCount 对象个数 *@param[in] pfCreateObj 创建CObj对象的函数,默认可以用DECLARE_DYN中的CreateObject *@return 0 success */ static CObjAllocator *CreateByGivenMemory(char *pszMemoryAddress, size_t uiMemorySize, size_t uiObjSize, int iObjCount, Function_CreateObj pfCreateObj); static size_t CountSize(size_t uiObjSize, int iObjCount); static CObjAllocator *ResumeByGivenMemory(char *pszMemoryAddress, size_t uiMemorySize, size_t uiObjSize, int iObjCount, Function_CreateObj pfCreateObj); public: //!初始化函数,将数据清空 int Initialize(); //!创建一个对象,成功返回对象ID,失败返回小于0的值 int CreateObject(); //!创建一个对象, 并指定其ID,成功返回对象ID,失败返回小于0的值 int CreateObjectByID(int iID); //!根据对象ID销毁一个对象,成功返回0 int DestroyObject(int iObjectID); //!根据ObjectID返回对象,必须保证该对象已被使用 CObj *GetObj(int iObjectID); //!根据ObjectID返回索引 CIdx *GetIdx(int iObjectID); //!获取已用对象链表头索引 inline int GetUsedHead() const return m_iUsedHeadIdx; //!获取空闲对象链表头索引 inline int GetFreeHead() const return m_iFreeHeadIdx; //获取已用对象个数 inline int GetUsedCount() const return m_iUsedCount; //获取空闲对象个数 inline int GetFreeCount() const return m_iObjCount - m_iUsedCount; //!在接口返回错误时,调用这个函数获取错误号 inline int GetErrorNO() const return m_iErrorNO; //获取对象分配类型 inline int GetObjAllocType() const return m_shObjAllocType; //!获取下一个对象 CObj *GetNextObj(int iObjectID); //!设置对象初始化指针 inline void SetCreateObjFunc(Function_CreateObj pfCreateObjFunc) m_pfCreateObjFunc = pfCreateObjFunc; private: //!设置错误号 inline void SetErrorNO(int iErrorNO) m_iErrorNO = iErrorNO; private: //这几个对象只有在构造函数时确定,后面不会更改 short m_shObjAllocType; //!<对象分配类型 size_t m_uiObjSize; //!<单个对象 int m_iObjCount; //!<最多分配的对象个数 CIdx *m_astIdxs; //!<索引数组,用于管理对象链表 CObj *m_pstObjBuffer; //!<分配的对象内存 Function_CreateObj m_pfCreateObjFunc; //!<在内存上创建CObj对象的函数,每个子类需要自己实现 //以下的对象会更改,调用Initialize初始化 int m_iErrorNO; //!<错误码 int m_iFreeHeadIdx; //!<空闲对象链表头索引 int m_iFreeTailIdx; //!<空闲对象链表尾索引 int m_iUsedHeadIdx; //!<已用对象链表头索引 int m_iUsedCount; //!<已用对象个数 ; //!基本对象类 class CObj public: CObj() virtual ~CObj() public: //!对象的初始化函数,在CObjAllocator创建对象时会调用,所以子类一定要实现 virtual int Initialize() = 0; //!显示对象函数,可重载 virtual int Show() const return 0; //!获取对象ID inline int GetObjectID() const return m_iObjectID; //!设置对象ID inline void SetObjectID(int iObjectID) m_iObjectID = iObjectID; virtual int Resume() return 0; private: int m_iObjectID; //!对象ID,即在CObjAllocator中的数组下标 friend int CObjAllocator::Initialize(); //!<在这个函数中需要直接赋值m_iObjectID,所以设为友元 ; #define DECLARE_DYN public: void *operator new(size_t uiSize, const void *pThis) throw(); static CObj *CreateObject(void *pMem); #define IMPLEMENT_DYN(class_name) void *class_name::operator new(size_t uiSize, const void *pThis) throw() if (!pThis) return NULL; return (void *)pThis; CObj *class_name::CreateObject(void *pMem) return (CObj *)new (pMem) class_name; // namespace ServerLib #endif //__OBJ_ALLOCATOR_HPP__ ///:~ /** *@file ObjAllocator.cpp *@author jasonxiong *@date 2009-12-14 *@version 1.0 *@brief 对象分配类的实现文件 * * */ #include <assert.h> #include "ErrorDef.hpp" #include "ObjAllocator.hpp" #include "SharedMemory.hpp" using namespace ServerLib; CIdx::CIdx() Initialize(); CIdx::~CIdx() int CIdx::Initialize() m_iNextIdx = -1; m_iPrevIdx = -1; m_iUseFlag = 0; m_pAttachedObj = NULL; return 0; CObjAllocator::CObjAllocator() //void* CObjAllocator::operator new(unsigned int uiSize, const void* pThis) throw() // // if(!pThis) // // return NULL; // // // return (void*)pThis; // CObjAllocator::CObjAllocator(size_t uiObjSize, int iObjCount, Function_CreateObj pfCreateObj) __ASSERT_AND_LOG(uiObjSize > 0 && iObjCount > 0 && pfCreateObj); m_shObjAllocType = EOAT_ALLOC_BY_SELF; m_iObjCount = iObjCount; m_uiObjSize = uiObjSize; m_pfCreateObjFunc = pfCreateObj; m_astIdxs = new CIdx[m_iObjCount]; size_t uiObjMemorySize = uiObjSize * iObjCount; char *pstObjMem = new char[uiObjMemorySize]; m_pstObjBuffer = (CObj *)pstObjMem; __ASSERT_AND_LOG(m_astIdxs && m_pstObjBuffer); Initialize(); size_t CObjAllocator::CountSize(size_t uiObjSize, int iObjCount) return sizeof(CObjAllocator) + uiObjSize * iObjCount + iObjCount * sizeof(CIdx); CObjAllocator *CObjAllocator::CreateByGivenMemory(char *pszMemoryAddress, size_t uiMemorySize, size_t uiObjSize, int iObjCount, Function_CreateObj pfCreateObj) if (pszMemoryAddress == NULL || uiObjSize <= 0 || iObjCount <= 0 || pfCreateObj == NULL) TRACESVR("%p, %d, %d, %p.\\n", pszMemoryAddress, (int)uiObjSize, iObjCount, pfCreateObj); return NULL; size_t uiSharedMemorySize = sizeof(CObjAllocator) + uiObjSize * iObjCount + iObjCount * sizeof(CIdx); if (uiSharedMemorySize > uiMemorySize) TRACESVR("ObjAllocator: alloc size %lu > sh size %lu.\\n", (unsigned long)uiSharedMemorySize, (unsigned long)uiMemorySize); return NULL; //在指定的内存地址上分配CObjAllocator CObjAllocator *pstObjAllocator = (CObjAllocator *)pszMemoryAddress; if (!pstObjAllocator) TRACESVR("ObjAllocator: pstObjAllocator is NULL.\\n"); return NULL; pstObjAllocator->m_uiObjSize = uiObjSize; pstObjAllocator->m_iObjCount = iObjCount; pstObjAllocator->m_pfCreateObjFunc = pfCreateObj; pstObjAllocator->m_shObjAllocType = EOAT_ALLOC_BY_SHARED_MEMORY; pstObjAllocator->m_astIdxs = (CIdx *)((unsigned char *)pszMemoryAddress + sizeof(CObjAllocator)); pstObjAllocator->m_pstObjBuffer = (CObj *)((unsigned char *)pszMemoryAddress + sizeof(CObjAllocator) + iObjCount * sizeof(CIdx)); pstObjAllocator->Initialize(); return pstObjAllocator; CObjAllocator *CObjAllocator::ResumeByGivenMemory(char *pszMemoryAddress, size_t uiMemorySize, size_t uiObjSize, int iObjCount, Function_CreateObj pfCreateObj) if ((NULL == pszMemoryAddress) || (uiObjSize <= 0) || (iObjCount <= 0)) return NULL; size_t uiSharedMemorySize = sizeof(CObjAllocator) + uiObjSize * iObjCount + sizeof(CIdx) * iObjCount; if (uiSharedMemorySize > uiMemorySize) return NULL; CObjAllocator *pstObjAllocator = (CObjAllocator *)pszMemoryAddress; if ((pstObjAllocator->m_uiObjSize != uiObjSize) || (pstObjAllocator->m_iObjCount != iObjCount)) return NULL; pstObjAllocator->m_shObjAllocType = EOAT_ALLOC_BY_SHARED_MEMORY; pstObjAllocator->m_astIdxs = (CIdx *)((unsigned char *)pszMemoryAddress + sizeof(CObjAllocator)); pstObjAllocator->m_pstObjBuffer = (CObj *)((unsigned char *)pszMemoryAddress + sizeof(CObjAllocator) + iObjCount * sizeof(CIdx)); int i; // 重新绑定obj和idx for (i = 0; i < iObjCount; ++i) // 调用placement-new, 恢复类的虚函数表. CObj *pstObj = (*pfCreateObj)((unsigned char *)pstObjAllocator->m_pstObjBuffer + uiObjSize * i); __ASSERT_AND_LOG(pstObj->GetObjectID() == i); pstObjAllocator->m_astIdxs[i].SetAttachedObj(pstObj); pstObjAllocator->SetCreateObjFunc(pfCreateObj); return pstObjAllocator; CObjAllocator *CObjAllocator::CreateBySharedMemory(const char *pszKeyFileName, const unsigned char ucKeyPrjID, size_t uiObjSize, int iObjCount, Function_CreateObj pfCreateObj) if (pszKeyFileName == NULL || uiObjSize <= 0 || iObjCount <= 0 || pfCreateObj == NULL) return NULL; CSharedMemory stSharedMemory; size_t uiSharedMemorySize = sizeof(CObjAllocator) + uiObjSize * iObjCount + iObjCount * sizeof(CIdx); int iRet = stSharedMemory.CreateShmSegment(pszKeyFileName, ucKeyPrjID, (int)uiSharedMemorySize); if (iRet) return NULL; //在共享内存的地址上分配CObjAllocator CObjAllocator *pstObjAllocator = (CObjAllocator *)stSharedMemory.GetFreeMemoryAddress(); if (!pstObjAllocator) return NULL; pstObjAllocator->m_uiObjSize = uiObjSize; pstObjAllocator->m_iObjCount = iObjCount; pstObjAllocator->m_pfCreateObjFunc = pfCreateObj; pstObjAllocator->m_shObjAllocType = EOAT_ALLOC_BY_SHARED_MEMORY; pstObjAllocator->m_astIdxs = (CIdx *)((unsigned char *)stSharedMemory.GetFreeMemoryAddress() + sizeof(CObjAllocator)); pstObjAllocator->m_pstObjBuffer = (CObj *)((unsigned char *)stSharedMemory.GetFreeMemoryAddress() + sizeof(CObjAllocator) + iObjCount * sizeof(CIdx)); return pstObjAllocator; CObjAllocator::~CObjAllocator() if (m_shObjAllocType == EOAT_ALLOC_BY_SELF) if (m_astIdxs) delete[] m_astIdxs; m_astIdxs = NULL; if (m_pstObjBuffer) char *pstObjMem = (char *)m_pstObjBuffer; delete[] pstObjMem; m_pstObjBuffer = NULL; int CObjAllocator::Initialize() if (m_pstObjBuffer == NULL || m_astIdxs == NULL) SetErrorNO(EEN_OBJ_ALLOCATOR__NULL_POINTER); return -1; if (m_iObjCount <= 0) SetErrorNO(EEN_OBJ_ALLOCATOR__INVALID_OBJ_COUNT); return -2; //初始化索引数组 int i; for (i = 0; i < m_iObjCount; ++i) m_astIdxs[i].Initialize(); m_astIdxs[i].SetPrevIdx(i - 1); m_astIdxs[i].SetNextIdx(i + 1); m_astIdxs[m_iObjCount - 1].SetNextIdx(-1); //初始化对象数组 for (i = 0; i < m_iObjCount; ++i) CObj *pstObj = (*m_pfCreateObjFunc)((unsigned char *)m_pstObjBuffer + m_uiObjSize * i); pstObj->m_iObjectID = i; m_astIdxs[i].SetAttachedObj(pstObj); m_iErrorNO = 0; m_iFreeHeadIdx = 0; m_iFreeTailIdx = m_iObjCount - 1; m_iUsedHeadIdx = -1; m_iUsedCount = 0; return 0; int CObjAllocator::CreateObject() if (m_pstObjBuffer == NULL || m_astIdxs == NULL) SetErrorNO(EEN_OBJ_ALLOCATOR__NULL_POINTER); return -1; if (m_iUsedCount >= m_iObjCount) SetErrorNO(EEN_OBJ_ALLOCATOR__OBJ_IS_FULL); return -2; if (m_iFreeHeadIdx < 0 || m_iFreeHeadIdx >= m_iObjCount) SetErrorNO(EEN_OBJ_ALLOCATOR__INVALID_OBJ_INDEX); return -3; //修改空闲链表 int iCurIdx = m_iFreeHeadIdx; m_iFreeHeadIdx = m_astIdxs[m_iFreeHeadIdx].GetNextIdx(); if (m_iFreeHeadIdx >= 0) m_astIdxs[m_iFreeHeadIdx].SetPrevIdx(-1); if (iCurIdx == m_iFreeTailIdx) m_iFreeTailIdx = -1; //挂到使用链表 m_astIdxs[iCurIdx].SetUsed(); m_astIdxs[iCurIdx].SetNextIdx(m_iUsedHeadIdx); m_astIdxs[iCurIdx].SetPrevIdx(-1); if (m_iUsedHeadIdx >= 0) m_astIdxs[m_iUsedHeadIdx].SetPrevIdx(iCurIdx); //初始化对象 m_iUsedHeadIdx = iCurIdx; CObj *pstObj = m_astIdxs[iCurIdx].GetAttachedObj(); if (NULL == pstObj) SetErrorNO(EEN_OBJ_ALLOCATOR__NULL_POINTER); return -4; #ifdef OBJ_MEMSET_ON_CREATE memset(pstObj, 0, m_uiObjSize); (*m_pfCreateObjFunc)((unsigned char *)pstObj); pstObj->SetObjectID(iCurIdx); #endif pstObj->Initialize(); ++m_iUsedCount; __ASSERT_AND_LOG(pstObj->GetObjectID() == iCurIdx); return iCurIdx; int CObjAllocator::CreateObjectByID(int iID) if (m_pstObjBuffer == NULL || m_astIdxs == NULL) SetErrorNO(EEN_OBJ_ALLOCATOR__NULL_POINTER); return -1; if (iID < 0 || iID >= m_iObjCount) SetErrorNO(EEN_OBJ_ALLOCATOR__INVALID_OBJ_INDEX); return -2; if (m_astIdxs[iID].IsUsed()) SetErrorNO(EEN_OBJ_ALLOCATOR__INVALID_OBJ_INDEX); return -3; //修改空闲链表 int iCurIdx = iID; int iPrevIdx = m_astIdxs[iCurIdx].GetPrevIdx(); int iNextIdx = m_astIdxs[iCurIdx].GetNextIdx(); if (iPrevIdx >= 0) m_astIdxs[iPrevIdx].SetNextIdx(iNextIdx); if (iNextIdx >= 0) m_astIdxs[iNextIdx].SetPrevIdx(iPrevIdx); if (iCurIdx == m_iFreeHeadIdx) m_iFreeHeadIdx = iNextIdx; if (iCurIdx == m_iFreeTailIdx) m_iFreeTailIdx = -1; //挂到使用链表 m_astIdxs[iCurIdx].SetUsed(); m_astIdxs[iCurIdx].SetNextIdx(m_iUsedHeadIdx); m_astIdxs[iCurIdx].SetPrevIdx(-1); if (m_iUsedHeadIdx >= 0) m_astIdxs[m_iUsedHeadIdx].SetPrevIdx(iCurIdx); m_iUsedHeadIdx = iCurIdx; // add by cary CObj *pstObj = m_astIdxs[iCurIdx].GetAttachedObj(); #ifdef OBJ_MEMSET_ON_CREATE memset(pstObj, 0, m_uiObjSize); (*m_pfCreateObjFunc)((unsigned char *)pstObj); pstObj->SetObjectID(iCurIdx); #endif pstObj->Initialize(); ++m_iUsedCount; __ASSERT_AND_LOG(pstObj->GetObjectID() == iCurIdx); return iCurIdx; int CObjAllocator::DestroyObject(int iObjectID) if (m_pstObjBuffer == NULL || m_astIdxs == NULL) SetErrorNO(EEN_OBJ_ALLOCATOR__NULL_POINTER); return -1; if (iObjectID >= m_iObjCount || iObjectID < 0 || m_iObjCount <= 0) SetErrorNO(EEN_OBJ_ALLOCATOR__INVALID_OBJ_INDEX); return -2; if (!m_astIdxs[iObjectID].IsUsed()) SetErrorNO(EEN_OBJ_ALLOCATOR__DESTROY_FREE_OBJ); return -3; //从已用链表中删除 int iCurIdx = iObjectID; int iPrevIdx = m_astIdxs[iCurIdx].GetPrevIdx(); int iNextIdx = m_astIdxs[iCurIdx].GetNextIdx(); if (iPrevIdx >= 0) m_astIdxs[iPrevIdx].SetNextIdx(iNextIdx); if (iNextIdx >= 0) m_astIdxs[iNextIdx].SetPrevIdx(iPrevIdx); if (iCurIdx == m_iUsedHeadIdx) m_iUsedHeadIdx = iNextIdx; //挂入空闲链表尾部 m_astIdxs[iObjectID].SetFree(); m_astIdxs[iObjectID].SetPrevIdx(m_iFreeTailIdx); m_astIdxs[iObjectID].SetNextIdx(-1); if (m_iFreeHeadIdx == -1) m_iFreeHeadIdx = iCurIdx; if (m_iFreeTailIdx >= 0) m_astIdxs[m_iFreeTailIdx].SetNextIdx(iCurIdx); m_iFreeTailIdx = iCurIdx; --m_iUsedCount; return 0; CObj *CObjAllocator::GetObj(int iObjectID) if (m_pstObjBuffer == NULL || m_astIdxs == NULL) SetErrorNO(EEN_OBJ_ALLOCATOR__NULL_POINTER); return NULL; if (iObjectID < 0 || iObjectID >= m_iObjCount) SetErrorNO(EEN_OBJ_ALLOCATOR__INVALID_OBJ_INDEX); return NULL; if (!m_astIdxs[iObjectID].IsUsed()) SetErrorNO(EEN_OBJ_ALLOCATOR__GET_FREE_OBJ); return NULL; return m_astIdxs[iObjectID].GetAttachedObj(); CIdx *CObjAllocator::GetIdx(int iObjectID) if (m_pstObjBuffer == NULL || m_astIdxs == NULL) SetErrorNO(EEN_OBJ_ALLOCATOR__NULL_POINTER); return NULL; if (iObjectID < 0 || iObjectID >= m_iObjCount) SetErrorNO(EEN_OBJ_ALLOCATOR__INVALID_OBJ_INDEX); return NULL; if (!m_astIdxs[iObjectID].IsUsed()) SetErrorNO(EEN_OBJ_ALLOCATOR__GET_FREE_OBJ); return NULL; return &m_astIdxs[iObjectID]; CObj *CObjAllocator::GetNextObj(int iObjectID) CIdx *pIdx = GetIdx(iObjectID); if (!pIdx) return NULL; int iNextObjIdx = pIdx->GetNextIdx(); return GetObj(iNextObjIdx);
源码地址:https://github.com/dai543103/ServerFramework-1/blob/master/001_ServerLib/BaseLibs/ObjAllocator.hpp
以上是关于内存对象管理器(基于数组和链表实现)的主要内容,如果未能解决你的问题,请参考以下文章