MFC六大關鍵技術之執行時型別識別

2021-04-09 07:25:56 字數 3619 閱讀 4451

執行時型別識別(rtti)即是程式執行過程中知道某個物件屬於某個類,我們平時用c++程式設計接觸的rtti一般是編譯器的rtti,即是在新版本的vc++編譯器裡面選用「使能rtti」,然後載入typeinfo.h檔案,就可以使用乙個叫typeid()的運運算元,它的地位與在c++程式設計中的sizeof()運運算元類似的地方(包含乙個標頭檔案,然後就有乙個熟悉好用的函式)。typdid()關鍵的地方是可以接受兩個型別的引數:乙個是類名稱,乙個是物件指標。所以我們判別乙個物件是否屬於某個類就可以象下面那樣:

if (typeid (classname)== typeid(*objectname))

象上面所說的那樣,乙個typeid()運運算元就可以輕鬆地識別乙個物件是否屬於某乙個類,但mfc並不是用typeid()的運運算元來進行動態型別識別,而是用一大堆令人費解的巨集。很多學員在這裡很疑惑,好象mfc在大部分地方都是故作神秘。使們大家程式設計時很迷惘,只知道在這裡加入一組巨集,又在那兒加入乙個對映,而不知道我們為什麼要加入這些東東。

其實,早期的mfc並沒有typeid()運運算元,所以只能沿用乙個老辦法。我們甚至可以想象一下,如果mfc早期就有template(模板)的概念,可能更容易解決rtti問題。

所以,我們要回到「古老」的年代,想象一下,要完成rtti要做些什麼事情。就好像我們在乙個新型(新型到我們還不認識)電器公司裡面,我們要識別哪個是電飯鍋,哪個是電磁爐等等,我們要檢視登記的各電器一系列的資訊,我們才可以比較、鑑別,那個東西是什麼!

要登記一系列的訊息並不是一件簡單的事情,大家可能首先想到用陣列登記物件。但如果用陣列,我們要定義多大的陣列才好呢,大了浪費空間,小了更加不行。所以我們要用另一種資料結構——鍊錶。因為鍊錶理論上可大可小,可以無限擴充套件。

struct cruntimeclass

; 鍊錶還應該有乙個表頭和乙個表尾,這樣我們在查煉表中各物件元素的資訊的時候才知道從**查起,到哪兒結束。我們還要註明本身是由哪能個類派生。所以我們的鍊錶類要這樣設計:

struct cruntimeclass

; 有了cruntimeclass結構後,我們就可以定義鍊錶了:

static cruntimeclass classcobject=;//這裡定義了乙個cruntimeclass物件,

因為classcobject無基類,所以m_pbaseclass為null。因為目前只有乙個元素(即目前沒有下一元素),所以m_pnextclass為null(表尾)。

至於pfirstclass(表頭),大家可能有點想不通,它到什麼地方去了。因為我們這裡並不想把classcobject作為煉表表頭,我們還要在前面插入很多的cruntimeclass物件,並且因為pfirstclass為static指標,即是說它不是屬於某個物件,所以我們在用它之前要先初始化:

cruntimeclass* cruntimeclass::pfirstclass=null;

現在我們可以在前面插入乙個cruntimeclass物件,插入之前我得重要申明一下:如果單純為了執行時型別識別,我們未必用到m_pnextclass指標(更多是在執行時建立時用),我們關心的是類本身和它的基類。這樣,查詢乙個物件是否屬於乙個類時,主要關心的是類本身及它的基類:

cruntimeclass classccmdtarget=;

cruntimeclass classcwnd=;

cruntimeclass classcview=;

好了,上面只是僅僅為乙個指標m_pbaseclass賦值(mfc中真正cruntimeclass有多個成員變數和方法),就連線成了鍊錶。假設我們現在已全部構造完成自己需要的cruntimeclass物件,那麼,這時候應該定義表頭。即要用pfirstclass指標指向我們最後構造的cruntimeclass物件——classcview。

cruntimeclass::pfirstclass=&classcview;

現在鍊錶有了,表頭表尾都完善了,問題又出現了,我們應該怎樣訪問每乙個cruntimeclass物件?要判斷乙個物件屬於某類,我們要從表頭開始,一直向表尾查詢到表尾,然後才能比較得出結果嗎。肯定不是這樣!

大家可以這樣想一下,我們構造這個鍊錶的目的,就是構造完之後,能夠按主觀地拿乙個cruntimeclass物件和鍊錶中的元素作比較,看看其中乙個物件中否屬於你指定的類。這樣,我們需要有乙個函式,乙個能返回自身型別名的函式getruntimeclass()。

上面簡單地說一下鍊錶的過程,但單純有這個鍊錶是沒有任何意義。回到mfc中來,我們要實現的是在每個需要有rtti能力的類中構造乙個cruntimeclass物件,比較乙個類是否屬於某個物件的時候,實際上只是比較cruntimeclass物件。

如何在各個類之中插入cruntimeclass物件,並且指定cruntimeclass物件的內容及cruntimeclass物件的鏈結,這裡起碼有十行的**才能完成。在每個需要有rtti能力的類設計中都要重複那十多行**是一件乏味的事情,也容易出錯,所以mfc用了兩個巨集代替這些工作,即declare_dynamic(類名)和implement_dynamic(類名,基類名)。從這兩個巨集我們可以看出在mfc名類中的cruntimeclass物件構造連線只有類名及基類名的不同!

到此,可能會有朋友問:為什麼要用兩個巨集,用乙個巨集不可以代換cruntimeclass物件構造連線嗎?個人認為肯定可以,因為巨集只是文字代換的遊戲而已。但我們在程式設計之中,標頭檔案與原始檔是分開的,我們要在標頭檔案頭宣告變數及方法,在原始檔里實具體實現。即是說我們要在標頭檔案中宣告:

public:

static cruntimeclass class*** //***為類名

virtual cruntime* getruntimeclass() const;

然後在原始檔裡實現:

cruntimeclass* ***::class***=;

cruntime* getruntimeclass() const;

//這裡不能直接返回&class***,因為static變數是類擁有而不是物件擁有。

我們一眼可以看出mfc中的declare_dynamic(類名)巨集應該這樣定義:

#define declare_dynamic(class_name) public: static cruntimeclass class##class_name;

virtual cruntimeclass* getruntimeclass() const;

其中##為連線符,可以讓我們傳入的類名前面加上class,否則跟原類同名,大家會知道產生什麼後果。

declare_dynamic(***)

巨集展開後就有了我們想要的:

public:

static cruntimeclass class*** //***為類名

virtual cruntime* getruntimeclass() const;

對於implement_dynamic(類名,基類名),看來也不值得在這裡代換文字了,大家知道它是知道回事,巨集展開後為我們做了什麼,再深究真是一點意義都沒有!

有了此鍊錶之後,就像有了一張存放各型別的網,我們可以輕而易舉地rtti。cobject有乙個函式bool iskindof(const cruntimeclass* pclass) const;,被它以下所有派生員繼承。

此函式實現如下:

bool cobject::iskindof(const cruntimeclass* pclass) const

return false;

}

MFC六大關鍵技術之執行時型別識別(RTTI)

什麼是rtti 執行時型別識別 rtti 即是程式執行過程中知道某個物件屬於某個類。實現過程 定義cruntimeclass結構體描述類別 struct cruntimeclass 為了實現執行時識別的目的mfc定義了下列兩個巨集 declare dynamic implement dynamic ...

MFC中的六大關鍵技術

1 mfc程式的初始化工作 在mfc中所有的類都 於乙個基類 cobject。mfc程式初始化過程中,其實就是虛函式的呼叫的過程,分清呼叫執行的到底是哪乙個具體的虛函式,是父類的虛函式,還是基類的虛函式,都是至關重要的。2 rtti執行時型別識別 mfc程式執行過程中需要對類的型別進行動態的判斷。在...

MFC六大關鍵技術之 三 動態建立

動態建立就是執行時建立指定類的物件,在mfc中大量使用。如框架視窗物件 視物件,還有文件物件都需要由文件模板類物件來動態的建立。我覺得這是每個mfc的學習者很希望理解的問題。初次接觸mfc的時候,很容易有這樣的迷惘。mfc的幾大類不用我們設計也就罷了,但最疑惑的是不用我們例項化物件。本來最直觀的理解...