zoukankan      html  css  js  c++  java
  • C++对象池

    前言

    • 大量使用的对象,重复的创建和销毁,很耗费性能,这个时候就要使用对象池技术。当物体使用时,如果对象池中有,从对象池中取出使用,没有就创建,不使用时候,将物体放回对象池,改变状态就是新的对象。
    • 常使用在飞机弹幕游戏中,玩家射击的时候,会创建很多子弹对象,当子弹对象碰到敌人时,会被销毁。不断的创建销毁对象时游戏帧数会下降,导致卡屏,所以可以使用对象池技术来解决。
    • 对象池根据类型可变,必须使用模板来实现,这样就会达到我有什么样类型,就会有什么样的对象池。

    效果和代码实现

    • 下图是程序运行的效果:

    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("对象池初始化
    ");
    		_InitObjectPool();
    	}
    	~ObjectPool()
    	{
    		MyPrintf("对象池析构
    ");
    		if (_pBuf != nullptr)
    		{
    			free(_pBuf);
    		}
    		_pBuf = nullptr;
    	}
    	//释放对象
    	void freeObject(void* pObject)
    	{
    		MyPrintf("释放对象%p
    ", 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
    ", ((char*)pReturn) + sizeof(ObjectNodeHeader));
    		}
    		else
    		{
    			pReturn = _pHeader;
    			_pHeader = _pHeader->pNext;
    			assert(0 == pReturn->nRef);
    			pReturn->nRef = 1;
    			MyPrintf("从对象池中申请对象%p
    ", ((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操作
    ");
    		//从对象池申请
    		return _PoolInstance().allocObject(size);
    	}
    	void operator delete(void* p)
    	{
    		MyPrintf("调用对象接管的delete操作
    ");
    		_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)这种遍历更香 ~!!
  • 相关阅读:
    jdk动态代理
    mysql-索引方案
    闭包的有点以及出现的内存泄露2016/4/12
    表单2016/4/8
    cursor
    同一个事件绑定不同的函数
    a:link visited hover active
    对于属性操作,加入属性,移除属性
    offset获取位置
    清除浮动6中方法
  • 原文地址:https://www.cnblogs.com/biu-we/p/13373049.html
Copyright © 2011-2022 走看看