c++ 反射機制:用類名作變數,動態創建對象
什麼是反射
從創建對象的角度上來看,狹義的說,比如有個 class A ,你能直接 new A() 來創建 對象。但是如果想根據字元串 「A」 來創建 class A 的對象,比如 使用 new 「A」 的形式來創建 對象,甚至 「A」 是個變數。 str = 「A」 , new str.
這種把 class 作為變數,又能在運行時創建對象的機制,就叫做反射。
大部分的高級編程語言,先天是支持反射的。用 lua 舉例
local AllTypes = {
Type1 = 1,
Type2 = 2,
Type3 = 3,
}
local typeClsHash = {}
typeClsHash[AllTypes.Type1] = Cls1
typeClsHash[AllTypes.Type2] = Cls2
typeClsHash[AllTypes.Type3] = Cls3
local theType = AllTypes.Type2
local cls = typeClsHash[theType]
local instance = cls:new()
instance:doSth()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如上面的代碼,可以動態的通過 theType 變數的值,來選擇 new 哪個 class 的對象。這樣代碼寫起來就比較靈活,省去了寫一大堆醜陋的 switch case,if/else 。
c++ 代碼示例
需求
遊戲裡面有很多UI,是 BaseMenu 的子類。包括MenuStageSelect, MenuHUD 等等。希望通過 MenuManager::pushMenu() 一個函數,寫明 Menu 的類型,動態創建和現實對應的 Menu.
原理
由於c++ 語言先天不支持反射機制,因此這種動態 new 不同 class 的實例的機制,在實現起來,必須依賴於函數指針。
用一個 std::map<>來存儲不同類型的Menu 的創建函數,key 是 menu類型,value 是創建的函數指針。
這樣才能實現反射機制,動態創建對象。
實現
MenuManager.h 定義所有 BaseMenu子類的枚舉
typedef enum
{
MENU_STAGE_SELECT,
MENU_HUD,
}MenuType;
1
2
3
4
5
MenuManager 定義一個 std::map 成員,用於存儲哪個類型的 menu ,由哪個函數來負責創建。
// Registered menus
typedef std::function<BaseMenu*()> MenuCreateFuncType;
std::map<MenuType, MenuCreateFuncType> _registerMenus;
1
2
3
每個 Menu 都需要提供自己的創建函數。在對應的類裡面,實現一個 static XXX* createInstance() 的方法。
例如 MenuHUD
在 MenuHUD.h 聲明 static MenuHUD* createInstance() 函數
class MenuHUD : public BaseMenu
{
public:
static MenuHUD* createInstance();
1
2
3
4
在 MenuHUD.cpp 里實現它
MenuHUD* MenuHUD::createInstance()
{
return new MenuHUD();
}
1
2
3
4
每個 BaseMenu 的子類都需要實現這個靜態函數。
在遊戲初始化時,註冊所有 Menu 的創建函數。
void MenuManager::registAllMenus()
{
_registerMenus[MenuType::MENU_STAGE_SELECT] = MenuStageSelect::createInstance;
_registerMenus[MenuType::MENU_HUD] = MenuHUD::createInstance;
}
1
2
3
4
5
這些準備工作做完之後,就可以在 MenuManager::pushMenu(MenuType menuType) 函數里,根據 Menu 類型,動態 new 出不同類型的 menu 的實例了。
void MenuManager::pushMenu(MenuType menuType)
{
BaseMenu* pInstance = _registerMenus[menuType]();
/*
Do other things...
*/
}
1
2
3
4
5
6
7
上面 map 的 key 是 Menu 的枚舉。如果希望用 Menu 的類名做變數,只需要把 key 的 枚舉,修改為 std::string ,在 registerAllMenus() 裡面,用類名作 key 即可。
※Monkey可視化工具開發
※springboot之定時任務
TAG:程序員小新人學習 |