1. 程式人生 > >C++ 實現反射(一)

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 的語法用巨集包裝起來而已…囧…

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