基類角色之對(duì)象管理器
版本:0.1
最后修改:2009-07-02
撰寫(xiě):李現(xiàn)民
問(wèn)題描述
C++程序設(shè)計(jì)中,保存一個(gè)生命周期不是由類對(duì)象自己維護(hù)的其它對(duì)象的指針通常是個(gè)壞主意,因?yàn)槌绦蜻壿嫼茈y判斷在使用該指針的時(shí)刻其所指對(duì)象是否已經(jīng)被銷毀。這種應(yīng)用需求很常見(jiàn),例如在網(wǎng)游設(shè)計(jì)中,由于華麗的裝備加載需要進(jìn)行大量硬盤(pán)I/O,因此加載過(guò)程通常由一個(gè)獨(dú)立的加載線程執(zhí)行,由于在裝備加載完成的時(shí)刻該玩家很可能已經(jīng)下線,因此加載線程就需要能夠去判斷此時(shí)玩家對(duì)象是否仍然有效。
為了解決該問(wèn)題,通常會(huì)設(shè)計(jì)一個(gè)PlayerManager類用于跟蹤管理當(dāng)前所有的玩家對(duì)象,而加載線程通過(guò)提供玩家id以確認(rèn)該玩家對(duì)象仍然存在。此種設(shè)計(jì)方案需要一個(gè)獨(dú)立的PlayerManager類,并提供一個(gè)全局的PlayerManager類對(duì)象以跟蹤當(dāng)前的所有玩家對(duì)象。
出于代碼復(fù)用的目的,我希望實(shí)現(xiàn)一個(gè)通用基類解決此類問(wèn)題。該基類需要為子類對(duì)象至少提供以下幾方面的能力:
-
為所有的對(duì)象分配一個(gè)全局唯一的index,通過(guò)該index能夠(盡可能快的)獲取到擁有該index的類對(duì)象(或NULL);
-
自動(dòng)跟蹤類對(duì)象的生成與銷毀,不需要手工編寫(xiě)額外代碼;
-
實(shí)現(xiàn)迭代器,提供遍歷當(dāng)前所有有效對(duì)象的能力;
-
提供“移除”接口,使得對(duì)象可以主動(dòng)要求放棄被對(duì)象管理器跟蹤;
-
各子類實(shí)現(xiàn)擁有完全獨(dú)立的管理器邏輯;
解決方案
將實(shí)現(xiàn)代碼保存為objectman.hpp,內(nèi)容如下:
/********************************************************************
created: 2009-06-29
author: lixianmin
purpose: base class for object manager
Copyright (C) 2009 - All Rights Reserved
*********************************************************************/
#ifndef _LIB_OBJECT_MAN_HPP_INCLUDED_
#define _LIB_OBJECT_MAN_HPP_INCLUDED_
#include <cassert>
#include <map>
namespace lib
{
template<typename T>
class objectman
{
public:
typedef int index_t; // 索引類型
typedef std::map<index_t, T*> object_map; // 容器類型
enum { INVAID_INDEX= 0}; // 無(wú)效索引
public:
// 迭代器
class iterator
{
public:
iterator(void): _iter(_mObjects.begin()){} // 構(gòu)造函數(shù)
bool has_next(void) const { return (_mObjects.end()!= _iter); } // 測(cè)試是否還有下一個(gè)對(duì)象
T* next(void) { return has_next()? (_iter++->second): NULL; } // 獲取下一個(gè)對(duì)象指針
private:
typename object_map::iterator _iter;
};
public:
// 構(gòu)造函數(shù)
objectman(void)
{
enable_index();
}
// copy 構(gòu)造函數(shù)
objectman(const objectman& rhs)
{
enable_index();
}
// 析構(gòu)函數(shù)
virtual ~objectman(void)
{
disable_index();
}
// 賦值操作符
objectman& operator= (const objectman& rhs)
{
}
// 通過(guò)索引獲取對(duì)象
static T* get_by_index(index_t index)
{
object_map::iterator iter= _mObjects.find(index);
if (_mObjects.end()!= iter)
{
T* pObject= iter->second;
assert(NULL!= pObject);
return pObject;
}
return NULL;
}
// 獲取對(duì)象索引
index_t get_index(void) const { return _idxObject; }
// 生成索引(使能被對(duì)象管理器遍歷到)
void enable_index(void)
{
_idxObject= ++_idxGenderator;
assert(get_index()!= INVAID_INDEX);
assert(_mObjects.find(get_index())== _mObjects.end());
_mObjects.insert(std::make_pair(get_index(), static_cast<T*>(this)));
}
// 移除索引(使不能被對(duì)象管理器遍歷到)
void disable_index(void)
{
if (get_index()!= INVAID_INDEX)
{
assert(_mObjects.find(get_index())!= _mObjects.end());
_mObjects.erase(get_index());
_idxObject= INVAID_INDEX;
}
}
private:
friend class iterator;
static object_map _mObjects; // 對(duì)象容器
static index_t _idxGenderator; // 索引發(fā)生器
index_t _idxObject; // 對(duì)象索引
};
template<typename T>
typename objectman<T>::object_map objectman<T>::_mObjects;
template<typename T>
typename objectman<T>::index_t objectman<T>::_idxGenderator= 0;
}
#endif
測(cè)試代碼
測(cè)試代碼如下:
#include <cassert>
#include "objectman.hpp"
// 聲明一個(gè)類
class Player:public lib::objectman<Player>
{
};
int main(int argc, char* argv[])
{
const int idxDisabled= 5;
// 生成對(duì)象
for (int i= 0; i< 10; ++i)
{
Player* pPlayer= new Player;
if (idxDisabled== pPlayer->get_index())
{
// 從對(duì)象管理器中移除該對(duì)象
pPlayer->disable_index();
}
}
//使用迭代器遍歷類對(duì)象
Player::iterator iter;
while(iter.has_next())
{
Player* pPlayer= iter.next();
const int idxPlayer= pPlayer->get_index();
// 斷言之:遍歷不到已經(jīng)移除的對(duì)象
assert(idxPlayer!= idxDisabled);
// 斷言之:可以通過(guò)idxPerson取得對(duì)象指針
assert(pPlayer== Player::get_by_index(idxPlayer));
// 回收對(duì)象
delete pPlayer;
pPlayer= NULL;
}
// 斷言之:所有對(duì)象均已被刪除
Player::iterator iter2;
assert(!iter2.has_next());
system("pause");
return 0;
}
vs2008下所有斷言均動(dòng)作通過(guò)。
已知問(wèn)題
-
在大量生成對(duì)象的情況下,index索引空間(代碼定義為int的范圍)有可能使用殆盡,甚至產(chǎn)生重復(fù),這會(huì)導(dǎo)致兩個(gè)對(duì)象擁有相同index的嚴(yán)重錯(cuò)誤;
-
std::map的查找速度不是特別另人滿意;