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 MenuCreateFuncType;
std::map _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 即可。

你可能感兴趣的:(c++ 反射机制: 用类名作变量,动态创建对象)