MFC六大關鍵技術之剖析(第二部分)

2021-05-22 08:07:09 字數 2980 閱讀 8385

二、執行時型別識別(rtti)

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

if (typeid (classname)== typeid(*objectname));//這裡定義了乙個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(類名)巨集之後,我們在標頭檔案裡寫上一句

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六大關鍵技術之剖析(第二部分)

追根究底,mfc六大關鍵技術之剖析 第二部分 小李先生 二 執行時型別識別 rtti 執行時型別識別 rtti 即是程式執行過程中知道某個物件屬於某個類,我們平時用c 程式設計接觸的rtti一般是編譯器的rtti,即是在新版本的vc 編譯器裡面選用 使能rtti 然後載入typeinfo.h檔案,就...

追根究底,MFC六大關鍵技術之剖析(第二部分)

追根究底,mfc六大關鍵技術之剖析 第二部分 小李先生 二 執行時型別識別 rtti 執行時型別識別 rtti 即是 程式執行過程中知道某個物件屬於某個類,我們平時用c 程式設計接觸的rtti一般是編譯器的rtti,即是在新版本的vc 編譯器裡面選用 使能 rtti 然後載入typeinfo.h檔案...

追根究底,MFC六大關鍵技術之剖析(第二部分)

追根究底,mfc六大關鍵技術之剖析 第二部分 小李先生 二 執行時型別識別 rtti 執行時型別識別 rtti 即是程式執行過程中知道某個物件屬於某個類,我們平時用c 程式設計接觸的rtti一般是編譯器的rtti,即是在新版本的vc 編譯器裡面選用 使能rtti 然後載入typeinfo.h檔案,就...