大量使用的對象,重覆的創建和銷毀,很耗費性能,這個時候就要使用對象池技術。 ...
前言
- 大量使用的對象,重覆的創建和銷毀,很耗費性能,這個時候就要使用對象池技術。當物體使用時,如果對象池中有,從對象池中取出使用,沒有就創建,不使用時候,將物體放回對象池,改變狀態就是新的對象。
- 常使用在飛機彈幕游戲中,玩家射擊的時候,會創建很多子彈對象,當子彈對象碰到敵人時,會被銷毀。不斷的創建銷毀對象時游戲幀數會下降,導致卡屏,所以可以使用對象池技術來解決。
- 對象池根據類型可變,必須使用模板來實現,這樣就會達到我有什麼樣類型,就會有什麼樣的對象池。
效果和代碼實現
- 下圖是程式運行的效果:
ObjectPool.h
#pragma once
#ifndef __OBJECTPOOL_H__
#define __OBJECTPOOL_H__
#include<cassert>
#include<mutex>
#ifdef _DEBUG
#ifndef MyPrintf
#define MyPrintf(...) printf(__VA_ARGS__)
#endif
#else
#ifndef MyPrintf
#define MyPrintf(...)
#endif
#endif // !_Debug
template<typename T,size_t nPoolSize>
class ObjectPool
{
private:
struct ObjectNodeHeader
{
ObjectNodeHeader* pNext;
char nRef;
bool bPool;
};
public:
ObjectPool()
{
MyPrintf("對象池初始化\n");
_InitObjectPool();
}
~ObjectPool()
{
MyPrintf("對象池析構\n");
if (_pBuf != nullptr)
{
free(_pBuf);
}
_pBuf = nullptr;
}
//釋放對象
void freeObject(void* pObject)
{
MyPrintf("釋放對象%p\n", pObject);
//計算出對象所在的對象信息頭部地址
ObjectNodeHeader* pObjNode = (ObjectNodeHeader*)((char*)pObject - sizeof(ObjectNodeHeader));
assert(1 == pObjNode->nRef);
pObjNode->nRef = 0;
if (pObjNode->bPool)
{
std::lock_guard<std::mutex> lock(_mutex);
pObjNode->pNext = _pHeader;
_pHeader = pObjNode;
}
else
{
//不在對象池
free(pObjNode);
}
}
//申請對象
void* allocObject(size_t size)
{
std::lock_guard<std::mutex> lock(_mutex);
ObjectNodeHeader* pReturn = nullptr;
if (nullptr == _pHeader)//記憶體耗盡
{
pReturn = (ObjectNodeHeader*)malloc(sizeof(T) + sizeof(ObjectNodeHeader));
pReturn->bPool = false;
pReturn->nRef = 1;
pReturn->pNext = nullptr;
MyPrintf("記憶體耗盡從系統中申請對象%p\n", ((char*)pReturn) + sizeof(ObjectNodeHeader));
}
else
{
pReturn = _pHeader;
_pHeader = _pHeader->pNext;
assert(0 == pReturn->nRef);
pReturn->nRef = 1;
MyPrintf("從對象池中申請對象%p\n", ((char*)pReturn) + sizeof(ObjectNodeHeader));
}
//跳過頭部的信息的位元組大小
return (char*)pReturn + sizeof(ObjectNodeHeader);
}
protected:
void _InitObjectPool()
{
assert(nullptr == _pBuf);
if (_pBuf) { return; }
//計算對象池的大小分配空間
size_t realsize = sizeof(ObjectNodeHeader) + sizeof(T);
size_t bufsize = nPoolSize * realsize;
_pBuf = (char*)malloc(bufsize);
//初始化對象節點數據
_pHeader = (ObjectNodeHeader*)_pBuf;
_pHeader->bPool = true;
_pHeader->nRef = 0;
_pHeader->pNext = nullptr;
//遍歷鏈表結構初始化
ObjectNodeHeader* pPerNode = _pHeader;
for (size_t i = 1; i < nPoolSize; ++i)
{
ObjectNodeHeader* pCurNode = (ObjectNodeHeader*)(_pBuf + i * realsize);
pCurNode->bPool = true;
pCurNode->nRef = 0;
pCurNode->pNext = nullptr;
pPerNode->pNext = pCurNode;
pPerNode = pCurNode;
}
}
private:
ObjectNodeHeader* _pHeader;
char* _pBuf;
std::mutex _mutex;
};
//對象介面模板
template<typename T,rsize_t nPoolSize>
class PoolBaseObject
{
public:
void* operator new(size_t size)
{
MyPrintf("調用對象接管的new操作\n");
//從對象池申請
return _PoolInstance().allocObject(size);
}
void operator delete(void* p)
{
MyPrintf("調用對象接管的delete操作\n");
_PoolInstance().freeObject(p);
}
template<typename ...Args>
static T* createObject(Args ... args)
{
//這裡做一些不方便在構造函數中做的事
T* obj = new T(args...);
return obj;
}
static void destroyObject(T* pobject)
{
delete pobject;
}
private:
static ObjectPool<T, nPoolSize>&_PoolInstance()
{
static ObjectPool< T, nPoolSize>selfPoolInstance;
return selfPoolInstance;
}
};
#endif // !__OBJECTPOOL_H__
額外補充
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
for (int& a:arr)
{
a = 666;
}
// for (int i = 0; i < 10; i++)
// {
// int& a = arr[i];
// a = 666;
// }
for (int a:arr)
{
cout << a << " ";
}
cout << endl;
//取裡面的值不加引用,但要改值要加引用
- 空類大小是1位元組,需要站位。
- 順序是new->構造->析構->delete
- foreach是只讀迴圈
- for (int a:arr)這種遍歷更香 ~!!