C++物件池
阿新 • • 發佈:2020-07-24
前言
- 大量使用的物件,重複的建立和銷燬,很耗費效能,這個時候就要使用物件池技術。當物體使用時,如果物件池中有,從物件池中取出使用,沒有就建立,不使用時候,將物體放回物件池,改變狀態就是新的物件。
- 常使用在飛機彈幕遊戲中,玩家射擊的時候,會建立很多子彈物件,當子彈物件碰到敵人時,會被銷燬。不斷的建立銷燬物件時遊戲幀數會下降,導致卡屏,所以可以使用物件池技術來解決。
- 物件池根據型別可變,必須使用模板來實現,這樣就會達到我有什麼樣型別,就會有什麼樣的物件池。
效果和程式碼實現
- 下圖是程式執行的效果:
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)這種遍歷更香 ~!!