天天看點

C++ 實作反射(一)

新部落格連結

反射,就是根據一個類名,即可根據類名擷取類資訊,建立新對象。反射在很多語言都天然支援,然而不包括 C++,但我們肯定會經常遇到這種根據類名生成對象的場景,這就需要我們自己動手來實作了。反正 C++ 這麼強大,一定沒有問題 :)

version 1

我們略做思考,就可以想到一種最簡單的方案:

if (class_name == "A") {
    return new A();
} else if (class_name == "B") {
    return new B();
} ...
           

當然,這個方案略做思考就可以否掉,這麼暴力毫無美感,代碼難以維護,每定義個新的類就需要添加一段新的 if,如果很多人共用和維護一定是場災難。

Version 2

然而其實我沒有放棄,看起來需要添加的代碼這麼相似,好像用個 template 可以解決?

#include <string>

template<typename BaseClassName, typename SubClassName>
BaseClassName* CreateObject() {
  return new SubClassName();
}

#define CLASS_OBJECT_CREATOR(base_class_name, sub_class_name) \
  CreateObject<base_class_name, sub_class_name>()
           

每次調用的時候隻要形如

base* p = CLASS_OBJECT_CREATOR(base, A);

這樣就好了,也許我們不知道傳入的是哪個派生類,但都可以用基類的指針儲存下來,像下面:

class base
{
  public:
    base() {}
    virtual test() { std::cout << "I'm class base!" << std::endl; }
    virtual ~base() {}
};
// 基類這裡再封裝一個宏,就不用每次都傳入基類名
#define CreateMyClass(class_name) \
  dynamic_cast<class_name*>(CLASS_OBJECT_CREATOR(base, class_name));

class A : public base
{
  public :
    A(){ std::cout << "A constructor!" << std::endl; }
    virtual test() { std::cout << "I'm class A!" << std::endl; }
    ~A() { std::cout << "A destructor!" << std::endl; }
};

class B : public base
{
  public :
    B(){ std::cout << "B constructor!" << std::endl; }
    virtual test() { std::cout << "I'm class B!" << std::endl; }
    ~B() { std::cout << "B destructor!" << std::endl; }
};

int main()
{
    A* p1 = CreateMyClass(A);
    delete p;
    B* p2 = CreateMyClass(B);
    delete p2;
    return 0;
}
           

看起來好像沒什麼問題,隻要調用

CreateMyClass(class_name)

就可以建立一個新的對象了。但總覺得哪裡不對…仔細一想,如果類名是個字元串呢?

CreateMyClass("A")

壓根就沒辦法處理呀!因為 C++ 本質上無法處理

new "A"

這種文法是以才說不支援反射,像上面這樣處理的話也根本沒有解決這個問題,隻是把 new 的文法用宏包裝起來而已…囧…

還是認真想想怎麼解決這個問題吧,見下篇