MFC六大核心機制之二 執行時型別識別(RTTI)

2021-06-27 12:23:55 字數 4117 閱讀 5500

typeid運運算元

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

c++** if

(typeid

(classname)==

typeid

(*objectname))

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

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

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

cruntimeclass鍊錶的設計

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

鍊錶是一種常用的資料結構,簡單地說,它是在乙個物件裡面儲存了指向下乙個同型別物件的指標。我們大體可以這樣設計我們的類:

c++**

struct

cruntimeclass ;

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

c++**

struct

cruntimeclass ;

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

c++**

static

cruntimeclass classcobject=;

//這裡定義了乙個cruntimeclass物件,因為classcobject無基類,所以m_pbaseclass為null。因為目前只有乙個元素(即目前沒有下一元素),所以m_pnextclass為null(表尾)。

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

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

c++**

cruntimeclass classccmdtarget=;

cruntimeclass classcwnd=;

cruntimeclass classcview=;

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

c++**

cruntimeclass::pfirstclass=&classcview;

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

類中構造cruntimeclass物件

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

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

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

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

c++**

public

: static

cruntimeclass class***

//***為類名

virtual

cruntime* getruntimeclass()

const;

然後在原始檔裡實現:

c++**

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

cruntime* getruntimeclass()

const

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

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

c++**

#define declare_dynamic(class_name) public: static cruntimeclass class##class_name; virtual cruntimeclass* getruntimeclass() const;

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

有了上面的declare_dynamic(類名)巨集之後,我們在標頭檔案裡寫上一句

declare_dynamic(***)

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

public:

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

virtual cruntime* getruntimeclass() const;

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

有了此鍊錶之後,就像有了一張存放各型別的網,我們可以輕而易舉地rtti。

iskindof函式

cobject有乙個函式bool iskindof(const cruntimeclass* pclass) const;,被它以下所有派生類繼承。

此函式實現如下:

c++**

bool

cobject::iskindof(

const

cruntimeclass* pclass)

const

return

false; }

說到這裡,執行時型別識別(rtti)算是完成了。

來自:

MFC六大核心機制之二 執行時型別識別(RTTI)

上一節講的是mfc六大核心機制之一 mfc程式的初始化,本節繼續講解mfc六大核心機制之二 執行時型別識別 rtti typeid運運算元 執行時型別識別 rtti 即是程式執行過程中知道某個物件屬於某個類,我們平時用c 程式設計接觸的rtti一般是編譯器的rtti,即是在新版本的vc 編譯器裡面選...

MFC的六大核心機制概述

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

初識MFC 執行時類資訊機制

1 執行時類資訊機制的作用 程式執行過程中,可以獲知物件的相關類的資訊 2 執行時類資訊機制的使用 2.1類必須派生自cobject類 2.2類內必須新增宣告巨集 declare dynamic 2.3類外必須新增實現巨集 implement dynamic cobject iskindof 來判斷...