1. 程式人生 > >02 MFC的執行型別識別(RTTI)

02 MFC的執行型別識別(RTTI)

什麼是RTTI?
RTTI(Run-Time Type Information ):通過執行時型別資訊程式能夠使用基類的指標或引用來檢查這些指標或引用所指的物件的實際派生型別。簡單來說,就是能在程式執行時幫我們知道某個物件屬於某個類;
我們先用一個示例來複習一下C++中判斷一個物件是不是屬於一個類的用法吧!

#include <iostream>

using namespace std;

class CAnimal{
public:
	CAnimal(){
		cout << "CAnimal" << endl;
	}
};

class CPlant{
public:
	CPlant(){
		cout << "CPlant" << endl;
	}
};

int main()
{
	CAnimal animal;
	if (typeid(animal) == typeid(CAnimal)){
		cout << "animal blongs to CAnimal!"  << endl;
	}
	if (typeid(animal) == typeid(CPlant)){
		cout << "animal blongs to CPlant!" << endl;
	}
	return 0;
}

C++既然有了RTTI,MFC為什麼還要弄自己的RTTI,這是因為出現時間先後問題,MFC早於C++的特性;那MFC怎麼實現RTTI的呢?
其主要通過三個下面三個關鍵的巨集和一個類來實現的:
DECLARE_DYNAMIC
IMPLEMENT_DYNAMIC
RUNTIME_CLASS
CRuntimeClass
如下簡單示例,我們就可以檢查物件否從某類派生,但此類必須從CObject派生(或間接)

/*
 *HelloMFC.h
 */
#ifndef _HELLO_MFC_
#define _HELLO_MFC_

class CMyApp : public CWinApp{
	DECLARE_DYNAMIC(CMyApp)
public:
	virtual BOOL InitInstance();
};

class CMainWindow : public CFrameWnd{
public:
	CMainWindow();
};

#endif

/*
 *HelloMFC.cpp
 */
#include <afxwin.h>
#include "HelloMFC.h"

CMyApp myApp;

IMPLEMENT_DYNAMIC(CMyApp, CWinApp)

BOOL CMyApp::InitInstance()
{
	int i = IsKindOf(RUNTIME_CLASS(CWinApp));
	m_pMainWnd = new CMainWindow;
	m_pMainWnd->ShowWindow(m_nCmdShow);
	m_pMainWnd->UpdateWindow();

	return TRUE;
}

CMainWindow::CMainWindow()
{
	Create(NULL, TEXT("Hello MFC"));
}

這樣我們就可以判斷我們的物件是否派生自某類了;接下來我們跟以下這幾個巨集,然後將巨集替換,還原出原來模樣如下:

/*
 *HelloMFC.h
 */
#ifndef _HELLO_MFC_
#define _HELLO_MFC_

class CMyApp : public CWinApp{
public: 
	static const CRuntimeClass classCMyApp;
	virtual CRuntimeClass* GetRuntimeClass() const; 
	//DECLARE_DYNAMIC(CMyApp)
public:
	virtual BOOL InitInstance();
protected:
	
};

class CMainWindow : public CFrameWnd{
public:
	CMainWindow();
};

#endif

/*
 *HelloMFC.cpp
 */
#include <afxwin.h>
#include "HelloMFC.h"

const CRuntimeClass CMyApp::classCMyApp = { 
    "CMyApp", sizeof(class CMyApp), 0xFFFF, NULL, 
    (CRuntimeClass*)(&CWinApp::classCWinApp), NULL,
    NULL
}; 

CRuntimeClass* CMyApp::GetRuntimeClass() const 
{ 
	return (CRuntimeClass*)(&CMyApp::classCMyApp);
}

CMyApp myApp;

BOOL CMyApp::InitInstance()
{
	int i = IsKindOf((CRuntimeClass*)(&CMyApp::classCMyApp));
	m_pMainWnd = new CMainWindow;
	m_pMainWnd->ShowWindow(m_nCmdShow);
	m_pMainWnd->UpdateWindow();

	return TRUE;
}

CMainWindow::CMainWindow()
{
	Create(NULL, TEXT("Hello MFC"));
}

我們再來看看CRuntimeClass 結構體,它記錄了本類類名,還有一個指向父類的指標, 用來構造一個類繼承連結串列用於遍歷;

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;
};

這時我們可以在Main函式處下斷點監視CMyApp::classCMyApp可知,在main函式前已經初始化好了,可見一個以本類開始OBject類結束的類繼承連結串列已經初始化好了;這下我們可以猜出它怎麼來判斷是不是繼承自某個類了吧,遍歷連結串列對比就好了,我們可以跟進IsKindOf進去看看,就知道真的是這樣;
在這裡插入圖片描述