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

