Symbian開發總結--RTTI的實現及原理說明
本文和大家重點學習一下Symbian開發總結--RTTI的實現及原理說明。RTTI(運行時類型信息)是被現代高級編程語言所普遍支持的特性之一,然而SymbianOSC++并不支持這個特性,這導致由Win32、JAVA轉向Symbian的開發人員或者代碼的移植都帶來很大的不便,本文將解決這個問題。
Symbian開發總結--RTTI的實現及原理說明
一、前言
RTTI(運行時類型信息)是被現代高級編程語言所普遍支持的特性之一,如C#中的“aisA”、JAVA中的“ainstanceofA”都屬于RTTI的范疇。然而SymbianOSC++并不支持這個特性,這導致由Win32、JAVA轉向Symbian的開發人員或者代碼的移植都帶來很大的不便,本文將解決這個問題。
二、什么是RTTI
Symbian開發中RTTI指的是“運行時類型識別(Run-TimeTypeIdentification)”或者“運行時類型信息(Run-TimeTypeInformation)”,程序能夠使用基類的指針或引用來檢查這些指針或引用所指的對象的實際派生類型。
隨著應用場合之不同﹐所需支持的RTTI范圍也不同。最單純的RTTI包括:
類識別(classidentification)──包括類名稱或ID。
繼承關系(inheritancerelationship)──支持執行時期的“往下變換類型”(downwardcasting),亦即動態變換類型(dynamiccasting)。
三、Symbian開發中的RTTI
由于Symbian系統以及它運行的硬件環境的限制,造成Symbian系統編程不能完全像一般C++程序設計隨心所欲,SymbianOSC++并不提供對RTTI的支持。所以,標準C++中的dynamic_cast<>、typeid()及type_info都是不被支持的。
四、移植MFC代碼實現RTTI
Symbian開發中VC++編譯器從4.0版才開始支持RTTI,但MFC4.x并未使用編譯器的能力完成其對RTTI的支持。MFC有自己一套沿用已久的辦法(從1.0版就開始了)。在此,我們借用MFC中實現RTTI的代碼,來完成對SymbianOSC++RTTI的支持。
關于MFC中RTTI的實現原理,侯捷的《深入淺出MFC》里已經有詳細的闡述,基本原理是使用幾個特殊的宏手動的在編譯期間確定一個對象繼承關系鏈表,在此不再說明具體原理。
我們移植的是VC++9.0中MFC實現RTTI的代碼,不使用侯捷在《深入淺出MFC》中所提供的模擬代碼。因為侯捷的代碼中存在非常多的“可寫的靜態數據”,將不能在SymbianDLL或者2nd版的APP中使用。然而,VC++9.0中的MFC代碼沒有存在以上問題,所以可以再任何Symbian代碼中使用。
壓縮包內包含兩個文件:Rtti.h、Rtti.cpp。將這兩個文件加入工程后,著手設計實現RTTI的類:
1、類的聲明:
Rtti.h頭文件中的CRttiBase是擁有RTTI特性的基礎類,此類相當于MFC中的CObject,它繼承自CBase,所有要實現RTTI特性的類都要從此類派生,并且在聲明加入一個特殊的宏:
- class CMyClass : public CRttiBase
- {
- DECLARE_DYNAMIC(CMyClass)
- ...
- };
注意:宏DECLARE_DYNAMIC中的第一個參數為當前類的類名:CMyClass。
聲明第二個類繼承自CMyClass,同樣的,要加上DECLARE_DYNAMIC宏:
- 1 class CMyClass1 : public CMyClass
- 2 {
- 3 DECLARE_DYNAMIC(CMyClass1)
- 4 ...
- 5 };
注意:實現RTTI的子類繼承自父類,而父類必須繼承自CRttiBase。
2、類的實現
在CMyClass和CMyClass1的實現源文件分別加入以下兩行代碼:
- 1 IMPLEMENT_DYNAMIC(CMyClass, CRttiBase);
- 2 IMPLEMENT_DYNAMIC(CMyClass1, CMyClass);
宏IMPLEMENT_DYNAMIC中的第一個參數為當前子類型,第二個參數為直接父類型,如:CMyClass的直接父類為CRttiBase,CMyClass1的直接父類為CMyClass。
3、使用RTTI特性
通過以上簡單兩個步驟,我們就能使用RTTI特性了,完整代碼:
- 1 class CMyClass : CRttiBase
- 2 {
- 3 DECLARE_DYNAMIC(CMyClass)
- 4 };
- 5
- 6 class CMyClass1 : CMyClass
- 7 {
- 8 DECLARE_DYNAMIC(CMyClass1)
- 9 };
- 10
- 11 class CMyClass2 : CRttiBase
- 12 {
- 13 DECLARE_DYNAMIC(CMyClass2)
- 14 };
- 15
- 16 IMPLEMENT_DYNAMIC(CMyClass, CRttiBase);
- 17 IMPLEMENT_DYNAMIC(CMyClass1, CMyClass);
- 18 IMPLEMENT_DYNAMIC(CMyClass2, CRttiBase);
- 19
- 20 LOCAL_C void MainL()
- 21 {
- 22 CMyClass1* mc1 = new (ELeave) CMyClass1;
- 23 TBool a = mc1->IsKindOf(RUNTIME_CLASS(CMyClass));
- 24 TBool b = mc1->IsKindOf(RUNTIME_CLASS(CRttiBase));
- 25 TBool c = mc1->IsKindOf(RUNTIME_CLASS(CMyClass2));
- 26 }
從代碼中可以看出CMyClass1的父類為CMyClass,CMyClass的父類為RTTI基類CRttiBase,而CMyClass2的基類也為CRttiBase,CMyClass1和CMyClass2沒有繼承關系。
所以,代碼第23至25行,abc的值依次為true、true、false。
CRttiBase::IsKindOf方法類似于C#中的“is”關鍵字、JAVA中的“instanceof”關鍵字,傳入的是某個類的運行時信息,而宏“RUNTIME_CLASS”獲取的是某個類的運行時信息“CRuntimeClass”。
4、運行時信息
“運行時信息”結構體CRuntimeClass在創建時將類的信息保存以便程序運行時查閱,其中包括類名、類大小、父類信息等。這些信息在宏IMPLEMENT_DYNAMIC內部,在程序編譯的時候就已經確定:
- 1 struct CRuntimeClass
- 2 {
- 3 const char* iClassName;
- 4 TInt iObjectSize;
- 5 TUint iSchema;
- 6 CRttiBase* (*iCreateObjectProc)();
- 7 CRuntimeClass* iBaseClass;
- 8 CRttiBase* CreateObject();
- 9 TBool IsDerivedFrom(const CRuntimeClass* aBaseClass) const;
- 10 CRuntimeClass* iNextClass;
- 11 };
注:CRuntimeClass可以理解為C#中的System.Type類型。
5、Symbian開發中獲取類和對象的運行時信息
獲取類的運行時信息使用宏RUNTIME_CLASS,如:
CRuntimeClass* classType = RUNTIME_CLASS(CMyClass);
注:以上代碼可以理解為C#中的“TypeclassType=typeof(CTestClass);”方法取類的類型信息。
獲取對象的運行時信息使用CRttiBase::GetRuntimeClass()方法,如:
CMyClass1* mc1 = new (ELeave) CMyClass1;
CRuntimeClass* rc = mc1->GetRuntimeClass();
注:以上代碼可以理解為C#中的“TypeclassType=theClass.GetType();”方法取對象的類型信息。
兩種方法均返回CRuntimeClass*。
6、通過運行時信息動態創建對象
大家可能會注意到CRuntimeClass有一個方法叫“CreateObject”,此方法能夠通過運行時信息動態的創建對象。這在某些實現比較復雜的功能往往是很有必要的。如:
有一個工廠,能夠生產不同的零件,而能夠生產的零件的類型是多種多樣的。
在沒有實現RTTI之前,我們可能會在工廠方法里寫一個很大的case語句,針對不同的零件類型進行判斷從而調用不同類的構造函數。
而實現了RTTI后,我們只需要保持一個零件類型和CRuntimeClass之間的哈希表,在工廠方法中向哈希表傳入零件類型,找到CRuntimeClass后調用CRuntimeClass::CreateObject()方法即可。
要實現動態創建對象,必須把函數聲明中的DECLARE_DYNAMIC改為DECLARE_DYNCREATE,把IMPLEMENT_DYNAMIC改為IMPLEMENT_DYNCREATE即可。如:
- 1 class CMyClass : CRttiBase
- 2 {
- 3 DECLARE_DYNCREATE(CMyClass)
- 4 };
- 5
- 6 IMPLEMENT_DYNCREATE(CMyClass, CRttiBase);
這樣,CMyClass的類型信息就能夠提供動態創建對象的功能了。
五、注意事項
CRttiBase是實現了對RTTI特性支持的父類,系統本身沒有提供對RTTI的支持。所以,要實現RTTI的類必須直接或間接的繼承自CRttiBase,這通常會對我們的設計造成很大的影響。如:如果一個類為活動對象,繼承自CActive,它又要實現RTTI特性,顯然以下聲明是錯誤的,因為CActive與CRttiBase都繼承自CBase:
class CMyActiveObject: public CActive, public CRttiBase {...}
在此有兩種方法解決:
采用Wrapper模式,封裝CActive并導出接口
通過修改rtti.h,使CRttiBase不繼承自CBase,每個基于RTTI的類都手動的指定基類CBase或其它,然后使用C++多重繼承的支持實現類的設計。
六、參考文獻
深入淺出MFC,侯捷
如何在運行時確定對象類型(RTTI)
SymbianOSC++高效編程
























