一个简单的对象管理类,管理对象的指针,使用了动态数组,可以动态扩容。
一.对象类
.h
/*!
* \file Obj.h
*
* \author puppet_master
* \date 八月 2015
*
* \对象类,有id,name两个属性
*/
#include "stdafx.h"
#pragma once
class CObj
{
public:
CObj(string name);
virtual ~CObj(void);
//获得或者设置对象属性的方法
void SetID(int id)
{
m_iObjId = id;
}
int GetID()
{
return m_iObjId;
}
void SetName(string name)
{
m_szObjName = name;
}
string GetName()
{
return m_szObjName;
}
//对象Tick
virtual void Tick();
private:
int m_iObjId; //对象ID
string m_szObjName; //对象名称
};
.cpp
#include "stdafx.h"
#include "Obj.h"
//在初始化列表中,对象的默认ID为INVALIDID,表示不可用,在进入对象管理器后,才会给分配真正的ID
CObj::CObj(string name)
: m_iObjId(INVALIDID),m_szObjName(name)
{
}
CObj::~CObj(void)
{
cout<<"Obj "<<m_iObjId<<" is destructed!"<<endl;
}
void CObj::Tick()
{
cout<<"Obj "<<m_iObjId<<" Tick"<<endl;
}
.h
/*!
* \file ObjManager.h
*
* \author puppet_master
* \date 八月 2015
*
* \对象管理类,使用动态数组进行管理,木有用vector
*/
#pragma once
#include "Obj.h"
class CObjManager
{
private:
const int DEFAULTSIZE; //默认大小
int m_iMaxSize; //当前最大对象个数
int m_iObjCount; //对象个数
CObj** m_ppObjArray; //管理对象指针的数组,这里是指针的指针
public:
CObjManager(void);
virtual ~CObjManager(void);
//初始化对象管理类,给出初始时的对象数组大小
void Init(int maxSize);
//添加对象,如果成功,返回对象id,否则返回INVALIDID(-1)
int AddObj(CObj* obj);
//根据对象ID删除对象(从对象管理器中清除对象,并释放内存)
bool DeleteObjById(int id);
//根据对象指针删除对象(从对象管理器中清除对象,并释放内存)
bool DeleteObjByPoint(CObj* obj);
//根据对象ID获得对象,有的话返回对象指针,否则返回NULL
CObj* GetObjById(int id);
//根据对象名字获得对象,有的话返回对象指针,否则返回NULL
CObj* GetObjByName(string name);
//遍历对象,并调用对象方法
virtual void Tick();
//删除所有对象
void RemoveAllObj();
//当数组不够大时,进行动态扩展操作
bool ExtendBuffer(int newSize);
//获得当前对象个数
int GetObjCount()
{
return m_iObjCount;
}
};
.cpp
#include "stdafx.h"
#include "ObjManager.h"
CObjManager::CObjManager(void):
DEFAULTSIZE(10), m_iObjCount(0), m_iMaxSize(DEFAULTSIZE), m_ppObjArray(NULL)
{
}
CObjManager::~CObjManager(void)
{
}
void CObjManager::Init(int maxSize)
{
//动态申请一个对象数组
m_ppObjArray = new CObj*[maxSize];
m_iMaxSize = maxSize;
//打印Log
if (m_ppObjArray)
cout<<"CObjManager Init OK"<<endl;
else
cout<<"CObjManager Init Failed"<<endl;
}
int CObjManager::AddObj(CObj* obj)
{
if (!obj)
return INVALIDID;
//如果不够大了,先动态扩展内存
if (m_iObjCount >= m_iMaxSize)
{
//动态将buffer扩展为两倍,扩展失败,直接返回非法ID
if (!ExtendBuffer(m_iMaxSize * 2))
return INVALIDID;
}
//直接将新建的对象ID设置为当前对象个数,然后对象个数+1
m_ppObjArray[m_iObjCount] = obj;
obj->SetID(m_iObjCount);
m_iObjCount++;
return m_iObjCount - 1;
}
bool CObjManager::DeleteObjById(int id)
{
if (id > m_iObjCount)
return false;
//交换数组中最后一个对象与被删除对象的位置
m_iObjCount--;
swap(m_ppObjArray[id],m_ppObjArray[m_iObjCount]);
m_ppObjArray[id]->SetID(id);
//清除最后一个对象(delete内存,并且指针置空)
SAFE_DELETE(m_ppObjArray[m_iObjCount]);
return true;
}
bool CObjManager::DeleteObjByPoint(CObj* obj)
{
int id = obj->GetID();
return DeleteObjById(id);
}
CObj* CObjManager::GetObjById(int id)
{
if (id > m_iObjCount)
return NULL;
return m_ppObjArray[id];
}
CObj* CObjManager::GetObjByName(string name)
{
for (int i = 0; i < m_iObjCount; i++)
{
if (m_ppObjArray[i]->GetName() == name)
return m_ppObjArray[i];
}
return NULL;
}
void CObjManager::RemoveAllObj()
{
for (int i = 0; i < m_iMaxSize; i++)
{
SAFE_DELETE(m_ppObjArray[i]);
}
SAFE_DELETE_ARRAY(m_ppObjArray);
m_iObjCount = 0;
}
void CObjManager::Tick()
{
for (int i = 0; i < m_iObjCount; i++)
{
m_ppObjArray[i]->Tick();
}
}
bool CObjManager::ExtendBuffer(int newSize)
{
//申请一个临时的指针指向原来的内存区域
CObj** ppTempBuffer = m_ppObjArray;
//申请一块新的更大的内存
m_ppObjArray = new CObj*[newSize];
//将原来的内存区域的内容拷贝到新的内存区域,由于管理的仅仅为对象指针,所以并不会有多大
if (ppTempBuffer)
{
memcpy(m_ppObjArray, ppTempBuffer, sizeof(CObj*) * m_iMaxSize);
}
//将多余区域置空
memset(&(m_ppObjArray[m_iObjCount]), 0, sizeof(CObj*) * (newSize - m_iMaxSize));
m_iMaxSize = newSize;
//清除原来的内存区域
SAFE_DELETE_ARRAY(ppTempBuffer);
return true;
}
三.使用对象管理类
stdafx
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <string>
using namespace std;
//定义一个不可用的ID,作为错误编号
#define INVALIDID -9999
//定义安全删除宏
#ifndef SAFE_DELETE
#define SAFE_DELETE(p) if((p) != NULL) {delete(p); (p) = NULL;}
#endif
//定义安全删除数组的宏
#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) if ((p) != NULL) {delete[] (p); (p) = NULL;}
#endif
// TODO: 在此处引用程序需要的其他头文件
main
/*!
* \file GameTest.cpp
*
* \author puppet_master
* \date 八月 2015
*
* \name 一个很好的使用原生数组管理对象的实例
* \point 动态数组,增加删除实例操作时间空间复杂度均为最小
\由于管理的均为对象的指针,管理较为方便,并且仅在大小不够时候才进行动态扩展
*/
#include "stdafx.h"
#include "Obj.h"
#include "ObjManager.h"
int _tmain(int argc, _TCHAR* argv[])
{
CObjManager* p_Manager = new CObjManager();
//初始化大小为5
p_Manager->Init(5);
//先插入20个对象
CObj* pObj = NULL;
for(int i = 0; i < 20; i++)
{
pObj = new CObj("HEHEH");
p_Manager->AddObj(pObj);
}
//查看Manager内存储对象
cout<<p_Manager->GetObjCount()<<endl;
p_Manager->Tick();
//删除ID为0的对象,查看对象个数
p_Manager->DeleteObjById(0);
cout<<p_Manager->GetObjCount()<<endl;
//增加一个对象,查看Manager对象
p_Manager->AddObj(new CObj("HAHAAH"));
cout<<p_Manager->GetObjCount()<<endl;
p_Manager->Tick();
//移除所有的对象,查看Manager对象
p_Manager->RemoveAllObj();
cout<<p_Manager->GetObjCount()<<endl;
p_Manager->Tick();
int i;
cin>>i;
return 0;
}
好吧,贴代码没啥意思。其实就是一个简单的练习,动态数组的使用。感觉这种实现有几个地方还是比较巧妙的。
1.主要的思想就是对象本身原本没有ID,仅有名字。然后,在对象被放在动态数组的时候,采用当前数组的元素个数作为新加入的对象的ID(数组索引永远比个数少1),然后将这个ID反注册给对象本身,这样,对象的ID就和它在对象管理器的数组中的ID是一致的。
2.对象管理类管理的并非对象本身,而是对象的指针,这样操作的话也比较方便。
3.可以动态扩容,在初始化的时候给管理类一个初始的大小,作为最开始动态数组的大小。当大小不够的时候,会重新申请一个2倍于原来大小的动态数组,然后将原来数组中的内容全部拷贝到新的数组中,再清除原来的数组。由于我们操作的是对象的指针,所以并不会太消耗性能,并且动态扩容的操作也不会经常触发的。
4.感觉最巧妙的就是删除对象的时候,将被删除对象的指针和当前数组最后一个对象指针位置互换,这时,ID位置的对象就变成了原本最后一个对象了,然后将这个ID再重新注册给对象。最后,再删除最后一个指针所指的对象,即我们要删除的那个对象。这样做的话,删除对象的时间复杂度是O(1)。
5.提供了根据名字查询对象的接口以及根据对象本身指针来从对象管理器中删除对象接口。