C++ 實現反射(一)
阿新 • • 發佈:2018-11-01
反射,就是根據一個類名,即可根據類名獲取類資訊,建立新物件。反射在很多語言都天然支援,然而不包括 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 的語法用巨集包裝起來而已…囧…
還是認真想想怎麼解決這個問題吧,見下篇