追根究底,mfc六大關鍵技術之剖析(二)
二、執行時型別識別(rtti)
執行時型別識別(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(類名)巨集之後,我們在標頭檔案裡寫上一句
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;
} 說到這裡,執行時型別識別(rtti)算是完成了。寫這篇文章的時候,我一直重感冒。我曾一度在想,究竟寫這東西是為了什麼。因為如果我把這些時間用在別的地方(甚至幫別人打打字),應該有數百元的報酬。
是什麼讓「嗜財如命」的我繼續寫下去?我想,無非是想交幾個計算機的朋友而已。計算機是大家公認高科技的東西,但學習它的朋友大多只能默默無聞,外界的朋友也不知道怎麼去認識你。程式設計師更不是「潮流」的東西,更加得不到別人的認可。
有一件個人認為很典型的事情,有一天,我跟乙個朋友到乙個單位裡面。裡面有乙個女打字員。朋友看著她熟練的指法,心悅誠服地說:「她的電腦水平比你的又高了乙個很高的層次!」,那個女的打字高手亦自豪地說:「我靠電腦為生,電腦水平肯定比你(指筆者)的好一點!換著是你,如果以電腦為生,我也不敢說好過你!」。雖然我想宣告我是計算機專業的,但我知道沒有理解,所以我只得客氣地點頭。
選擇電腦「潮流」的東西實際是選擇了平凡,而選擇做程式設計師就是選擇了孤獨!幸好我不是乙個專門的程式設計師,但即使如此,我願意做你們的朋友,因為我愛你們
追根究底,MFC六大關鍵技術剖析(三)
三 動態建立 動態建立就是執行時建立指定類的物件,在mfc中大量使用。如框架視窗物件 視物件,還有文件物件都需要由文件模板類物件來動態的建立。我覺得這是每個mfc的學習者很希望理解的問題。初次接觸mfc的時候,很容易有這樣的迷惘。mfc的幾大類不用我們設計也就罷了,但最疑惑的是不用我們例項化物件。本...
追根究底,MFC六大關鍵技術之剖析(2)
追根究底,mfc六大關鍵技術之剖析 第二部分 小李先生 二 執行時型別識別 rtti 執行時型別識別 rtti 即是程式執行過程中知道某個物件屬於某個類,我們平時用c 程式設計接觸的rtti一般是編譯器的rtti,即是在新版本的vc 編譯器裡面選用 使能rtti 然後載入typeinfo.h檔案,就...
追根究底,MFC六大關鍵技術之剖析(第二部分)
追根究底,mfc六大關鍵技術之剖析 第二部分 小李先生 二 執行時型別識別 rtti 執行時型別識別 rtti 即是程式執行過程中知道某個物件屬於某個類,我們平時用c 程式設計接觸的rtti一般是編譯器的rtti,即是在新版本的vc 編譯器裡面選用 使能rtti 然後載入typeinfo.h檔案,就...