運行時類信息 (CRuntimeClass 類)
動態類型識別(Runtime Type Infomation, RTTI):在程序運行的過程中辨別對象是否屬於特定類的技術。
動態創建:如果爲每個類都寫一個創建該類的全局函數的話,就能夠依靠從文件或用戶的輸入中取得此函數的內存地址,從而創建用戶動態指定的類。
一個類支持運行期識別能力需要兩個條件:
1.含有一個CRuntimeClass類型的靜態變量;
2.一個可以取得CRuntimeClass對象地址的虛函數GetRuntimeClass。
CRuntimeClass類的結構(在頭文件afx.h中):
struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName;
int m_nObjectSize;
UINT m_wSchema; // schema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
#ifdef _AFXDLL
CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
#else
CRuntimeClass* m_pBaseClass;
#endif
// Operations
CObject* CreateObject();
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;
// dynamic name lookup and creation
static CRuntimeClass* PASCAL FromName(LPCSTR lpszClassName);
static CRuntimeClass* PASCAL FromName(LPCWSTR lpszClassName);
static CObject* PASCAL CreateObject(LPCSTR lpszClassName);
static CObject* PASCAL CreateObject(LPCWSTR lpszClassName);
// Implementation
void Store(CArchive& ar) const;
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);
// CRuntimeClass objects linked together in simple list
CRuntimeClass* m_pNextClass; // linked list of registered classes
const AFX_CLASSINIT* m_pClassInit;
};
要想使所有的類都具有運行期識別和動態創建的特性,必須有一個類作爲繼承體系的頂層,將它命名爲CObject,它是所有類的基類。
CObject類結構:
// class CObject is the root of all compliant objects
class AFX_NOVTABLE CObject
{
public:
// Object model (types, destruction, allocation)
virtual CRuntimeClass* GetRuntimeClass() const;
virtual ~CObject() = 0; // virtual destructors are necessary
// Diagnostic allocations
void* PASCAL operator new(size_t nSize);
void* PASCAL operator new(size_t, void* p);
void PASCAL operator delete(void* p);
void PASCAL operator delete(void* p, void* pPlace);
#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
// for file name/line number tracking using DEBUG_NEW
void* PASCAL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);
void PASCAL operator delete(void *p, LPCSTR lpszFileName, int nLine);
#endif
// Disable the copy constructor and assignment by default so you will get
// compiler errors instead of unexpected behaviour if you pass objects
// by value or assign objects.
protected:
CObject();
private:
CObject(const CObject& objectSrc); // no implementation
void operator=(const CObject& objectSrc); // no implementation
// Attributes
public:
BOOL IsSerializable() const;
BOOL IsKindOf(const CRuntimeClass* pClass) const;
// Overridables
virtual void Serialize(CArchive& ar);
#if defined(_DEBUG) || defined(_AFXDLL)
// Diagnostic Support
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
// Implementation
public:
static const CRuntimeClass classCObject; //運行時類信息
#ifdef _AFXDLL
static CRuntimeClass* PASCAL _GetBaseClass();
static CRuntimeClass* PASCAL GetThisClass();
#endif
};
幾個幫助簡化代碼的宏:
1. RUNTIME_CLASS :爲了方便訪問類的CRuntimeClass結構而定義的宏。
#define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
2. DECLARE_DYNAMIC:聲明支持動態類信息
#define DECLARE_DYNAMIC(class_name) \
public: \
static const CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \
3. IMPLEMENT_RUNTIMECLASS: 實例化CRuntimeClass對象,爲了方便定義IMPLEMENT_DYNAMIC 宏
#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \
AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
RUNTIME_CLASS(base_class_name), NULL, class_init }; \
CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); }
4. IMPLEMENT_DYNAMIC:
#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)
有了上面的幾個宏定義,現在只需要使用DECLARE_DYNAMIC 和 IMPLEMENT_DYNAMIC兩個宏就可以使CObject的派生類擁有動
態識別的功能。
一個例子:向自己的類中添加動態類型識別的功能。
class CPerson : public CObject
{
//聲明支持動態類信息
DECLARE_DYNAMIC(CPerson)
};
//實現動態類信息
IMPLEMENT_DYNAMIC(CPerson, CObject)
int main()
{
CObject *pMyObject = new CPerson;
if(pMyObject->IsKindOf(RUNTIME_CLASS(CPerson)))
{
CPerson * pMyPerson = (CPerson*)pMyObject;
cout<<"a CPerson Object!\n";
delete pMyPerson;
}
else
delete pMyObject;
return 0;
}
動態創建:建立在動態識別基礎之上
在動態識別的基礎上,向類裏面添加一個創建該類的靜態成員函數,就可以完成動態創建的功能了。依靠上面的兩個宏
(DECLARE_DYNAMIC 和 IMPLEMENT_DYNAMIC), 可以很容易的定義出支持動態創建的一組宏,將它們命名爲
DECLARE_DYNCREATE 和 IMPLEMENT_DYNCREATE。
1. DECLARE_DYNCREATE:
#define DECLARE_DYNCREATE(class_name) \
DECLARE_DYNAMIC(class_name) \
static CObject* PASCAL CreateObject();
2. IMPLEMENT_DYNCREATE:
#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \
CObject* PASCAL class_name::CreateObject() \
{ return new class_name; } \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \
class_name::CreateObject, NULL)
怎麼使用?和動態類型識別宏的使用差不多,例如:
class CPerson : public CObject
{
//聲明支持動態創建
DECLARE_DYNCREATE(CPerson);
};
//實現動態創建
IMPLEMENT_DYNCREATE(CPerson, CObject)