1. 程式人生 > >c++ 反射機制: 用類名作變數,動態建立物件

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()

如上面的程式碼,可以動態的通過 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;

MenuManager 定義一個 std::map 成員,用於儲存哪個型別的 menu ,由哪個函式來負責建立。

// Registered menus
typedef std::function<BaseMenu*()> MenuCreateFuncType;
std::map<MenuType, MenuCreateFuncType> _registerMenus;

每個 Menu 都需要提供自己的建立函式。在對應的類裡面,實現一個 static XXX* createInstance() 的方法。

例如 MenuHUD
在 MenuHUD.h 宣告 static MenuHUD* createInstance() 函式

class MenuHUD : public BaseMenu
{
public:
    static MenuHUD* createInstance();

在 MenuHUD.cpp 裡實現它

MenuHUD* MenuHUD::createInstance()
{
    return new MenuHUD();
}

每個 BaseMenu 的子類都需要實現這個靜態函式。

在遊戲初始化時,註冊所有 Menu 的建立函式。

void MenuManager::registAllMenus()
{
    _registerMenus[MenuType::MENU_STAGE_SELECT] = MenuStageSelect::createInstance;
    _registerMenus[MenuType::MENU_HUD] = MenuHUD::createInstance;
}

這些準備工作做完之後,就可以在 MenuManager::pushMenu(MenuType menuType) 函式裡,根據 Menu 型別,動態 new 出不同型別的 menu 的例項了。

void MenuManager::pushMenu(MenuType menuType)
{
    BaseMenu* pInstance = _registerMenus[menuType]();
    /*
        Do other things...
    */
}

上面 map 的 key 是 Menu 的列舉。如果希望用 Menu 的類名做變數,只需要把 key 的 列舉,修改為 std::string ,在 registerAllMenus() 裡面,用類名作 key 即可。