对象池设计¶
- 对象池:对象池具有预先分配一些对象,使用(new对象)时从池中获取对象,释放(delete对象)时归还池中的功能。
- 优点:减少对象创建销毁的时间,和额外的内存
- 缺点:初始内存占用增多
- 使用场景:对象创建频繁的,成本比较大的
1 对象池架构¶
对象池通常由以下2部分组成: 1. 对象池:实现对象分配、回收 2. 对象池管理:管理对象池的生命周期、扩容、缩容等
2 对象池类设计¶
- 重写对象创建和销毁操作,创建优先从池中获取,销毁则将对象置为可用
- 支持对象池管理类的扩缩容功能
- 实现锁机制,保证线程安全
3 对象池管理类设计¶
- 单例类
- 创建、销毁、管理对象池功能
- 从对象池获取、归还对象功能
4 源码设计¶
- 对象池代码:
#ifndef _ObjectPoolBase_hpp_
#define _ObjectPoolBase_hpp_
#include<stdlib.h>
#include<assert.h>
#include<mutex>
#ifdef _DEBUG
#ifndef xPrintf
#include<stdio.h>
#define xPrintf(...) printf(__VA_ARGS__)
#endif
#else
#ifndef xPrintf
#define xPrintf(...)
#endif
#endif // _DEBUG
//模板给对象池提供参数接口
template<class Type, size_t nPoolSzie>
//对象池的实现
class CELLObjectPool
{
public:
CELLObjectPool()
{
initPool();
}
~CELLObjectPool()
{
if (_pBuf)
delete[] _pBuf;
}
private:
//NodeHeader是每个对象的描述信息
class NodeHeader
{
public:
//下一块位置
NodeHeader* pNext;
//内存块编号
int nID;
//引用次数
char nRef;
//是否在内存池中
bool bPool;
private:
//预留
char c1;
char c2;
};
public:
//释放对象内存
void freeObjMemory(void* pMem)
{
//首地址往前偏移,对象和对象描述信息的内存一起释放
NodeHeader* pBlock = (NodeHeader*)((char*)pMem - sizeof(NodeHeader));
xPrintf("freeObjMemory: %llx, id=%d\n", pBlock, pBlock->nID);
assert(1 == pBlock->nRef);
//内存在对象池的部分释放之后,指针回到第一个未分配的对象
if (pBlock->bPool)
{
std::lock_guard<std::mutex> lg(_mutex);
if (--pBlock->nRef != 0)
{
return;
}
pBlock->pNext = _pHeader;
_pHeader = pBlock;
}
else {
if (--pBlock->nRef != 0)
{
return;
}
//不在对象池的直接释放内存
delete[] pBlock;
}
}
//申请对象内存,优先向内存池申请内存,内存池不够在向系统申请内存
void* allocObjMemory(size_t nSize)
{
std::lock_guard<std::mutex> lg(_mutex);
NodeHeader* pReturn = nullptr;
//如果对象池已经满了,数量达到上限,需要向系统申请内存
if (nullptr == _pHeader)
{
//计算对象池大小=对象数量*(一个对象内存大小+对象描述信息内存大小)
pReturn = (NodeHeader*)new char[sizeof(Type) + sizeof(NodeHeader)];
pReturn->bPool = false;
pReturn->nID = -1;
pReturn->nRef = 1;
pReturn->pNext = nullptr;
}
else {
pReturn = _pHeader;
_pHeader = _pHeader->pNext;
assert(0 == pReturn->nRef);
pReturn->nRef = 1;
}
xPrintf("allocObjMemory: %llx, id=%d, size=%d\n", pReturn, pReturn->nID, nSize);
return ((char*)pReturn + sizeof(NodeHeader));
}
private:
//初始化对象池
void initPool()
{
//断言
assert(nullptr == _pBuf);
//对象池不为空(已经初始化过),不初始化直接返回
if (_pBuf)
return;
//计算对象池大小=对象数量*(一个对象内存大小+对象描述信息内存大小)
size_t realSzie = sizeof(Type) + sizeof(NodeHeader);
size_t n = nPoolSzie * realSzie;
//申请池的内存
_pBuf = new char[n];
//初始化内存池
_pHeader = (NodeHeader*)_pBuf;
_pHeader->bPool = true;
_pHeader->nID = 0;
_pHeader->nRef = 0;
_pHeader->pNext = nullptr;
//遍历内存块进行初始化
NodeHeader* pTemp1 = _pHeader;
for (size_t n = 1; n < nPoolSzie; n++)
{
NodeHeader* pTemp2 = (NodeHeader*)(_pBuf + (n* realSzie));
pTemp2->bPool = true;
pTemp2->nID = n;
pTemp2->nRef = 0;
pTemp2->pNext = nullptr;
pTemp1->pNext = pTemp2;
pTemp1 = pTemp2;
}
}
private:
//描述信息块地址
NodeHeader* _pHeader;
//对象池内存缓存区地址
char* _pBuf;
//多线程使用需要加锁
std::mutex _mutex;
};
//模板类给主函数使用对象池提供接口
template<class Type, size_t nPoolSzie>
//使用对象池的类
class ObjectPoolBase
{
public:
//重载给对象申请内存的new操作
void* operator new(size_t nSize)
{
return objectPool().allocObjMemory(nSize);
}
//重载给对象释放内存的delete操作
void operator delete(void* p)
{
objectPool().freeObjMemory(p);
}
//创建对象,用模板类提供对外的可变参数的接口
//这里的模板为啥要用可变参数?
//这里创建一个对象的时候还可能带有初始化的参数,这个参数的个数是不一样的,所有模板的参数需要变参
//
template<typename ...Args>
static Type* createObject(Args ... args)
{ //不定参数 可变参数
Type* obj = new Type(args...);
//可以做点其它的事情,比如说对象的放逐和驱逐
return obj;
}
//销毁对象
static void destroyObject(Type* obj)
{
delete obj;
}
private:
//定义不同类型的对象池
typedef CELLObjectPool<Type, nPoolSzie> ClassTypePool;
//单例-饿汉模式,实例化一个对象池
static ClassTypePool& objectPool()
{ //静态CELLObjectPool对象
static ClassTypePool sPool;
return sPool;
}
};
#endif
- 测试代码
#include<stdlib.h>
#include<iostream>
#include<thread>
#include<mutex>//锁
#include<memory>
#include"ObjectPoolBase.hpp"
using namespace std;
//10是池内对象的最大数量,通过继承ObjectPoolBase来使用对象池
class ClassA : public ObjectPoolBase<ClassA, 10>
{
public:
ClassA(int n)
{
num = n;
printf("ClassA\n");
}
~ClassA()
{
printf("~ClassA\n");
}
public:
int num = 0;
};
class ClassB : public ObjectPoolBase<ClassB, 10>
{
public:
ClassB(int n, int m)
{
num = n * m;
printf("ClassB\n");
}
~ClassB()
{
printf("~ClassB\n");
}
public:
int num = 0;
};
int main()
{
{
//对象池的使用过程是在使用new的时候,因为你要创建的类继承了对象池管理的类,
//对象池管理类会根据你设计的对象数量创建(初始化)对象池,
//进入对象池的时候首先判断该对象池是否初始化,初始化过说明该对象池已经存在
//未初始化说明还没有该类型的对象池
//如果该对象池没有初始化,就进行初始化(创建一个对象池)
//已经初始化就直接从该对象池中分配一个对象
//然后通过new操作在对象池中分配一个已经创建好的对象
//再根据对象传进来的参数,确定对象池允许有几个对象
//然后申请相应大小的内存,逐个以链表的形式进行初始化
//两种创建对象的方法
//直接用new创建对象,把new和delete暴露在外,使用不灵活,初始化对象不方便
ClassA* a1 = new ClassA(5);
//delete a1;
//把new和delete封装成一个函数,可以更灵活的对对象进行操作,比如在创建的时候初始化对象
ClassA* a2 = ClassA::createObject(6);
//ClassA::destroyObject(a2);
delete a1;
ClassA::destroyObject(a2);
ClassB* b1 = new ClassB(5, 6);
delete b1;
ClassB* b2 = ClassB::createObject(5, 6);
ClassB::destroyObject(b2);
}
system("pause");
return 0;
}