5第五講——運行時類型識別_第1頁
5第五講——運行時類型識別_第2頁
5第五講——運行時類型識別_第3頁
5第五講——運行時類型識別_第4頁
5第五講——運行時類型識別_第5頁
已閱讀5頁,還剩19頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

1、運行時類型識別運行時類型識別第五講第五講 運行時類型識別運行時類型識別 rtti的概念的概念 rtti的兩種使用方法的兩種使用方法合理使用合理使用rtti運行時類型識別運行時類型識別21989年,由于異常處理的引入,年,由于異常處理的引入,c+必須具有運必須具有運行時類型識別能力,于是導致了行時類型識別能力,于是導致了 rtti 機制的誕生。機制的誕生。rtti 機制不僅滿足了異常處理的需要,還解決了虛機制不僅滿足了異常處理的需要,還解決了虛函數(shù)的難題。函數(shù)的難題?!坝辛擞辛藃tti 機制之后,系統(tǒng)就能在運行時查詢機制之后,系統(tǒng)就能在運行時查詢一個多態(tài)指針或引用指向的具體對象的類型了。一個多態(tài)

2、指針或引用指向的具體對象的類型了?!保╨ippman 語)語)rtti 機制的核心:機制的核心:typeid 運算符運算符 關于關于 rtti先激活先激活rtti:在在project 菜單中,菜單中,選選setting項,項,單擊單擊c/c+標簽,標簽,category列表中,列表中,選選c+ language,單擊單擊enable rtti后后 ok.運行時類型識別運行時類型識別 運行時類型識別(運行時類型識別(run-time type identification , rtti)是指)是指,在只有一個指向基類的指針或引用時,確在只有一個指向基類的指針或引用時,確定所指對象的準確類型的操作

3、。定所指對象的準確類型的操作。 一般情況下,虛函數(shù)機制并不需要一個類的確切一般情況下,虛函數(shù)機制并不需要一個類的確切類型,就可以實現(xiàn)對那種類型的對象實施正確行為。類型,就可以實現(xiàn)對那種類型的對象實施正確行為。但是,在很多情況下,虛函數(shù)無法克服本身的不能反但是,在很多情況下,虛函數(shù)無法克服本身的不能反映確切類型的局限。不可避免要對對象類型進行動態(tài)映確切類型的局限。不可避免要對對象類型進行動態(tài)判斷,也就是動態(tài)類型的偵測識別。判斷,也就是動態(tài)類型的偵測識別。1. rtti的概念的概念運行時類型識別運行時類型識別 和很多其他語言一樣,和很多其他語言一樣,c+是一種靜態(tài)類型語言。是一種靜態(tài)類型語言。其數(shù)

4、據(jù)類型是在編譯期就確定的,不能在運行時更改。其數(shù)據(jù)類型是在編譯期就確定的,不能在運行時更改。然而由于面向?qū)ο蟪绦蛟O計中多態(tài)性的要求,然而由于面向?qū)ο蟪绦蛟O計中多態(tài)性的要求,c+中中的指針或引用的指針或引用(reference)本身的類型,可能與它實際本身的類型,可能與它實際代表代表(指向或引用指向或引用)的類型并不一致。我們往往需要將一的類型并不一致。我們往往需要將一個多態(tài)指針轉(zhuǎn)換為其實際指向?qū)ο蟮念愋?,就需要知個多態(tài)指針轉(zhuǎn)換為其實際指向?qū)ο蟮念愋?,就需要知道運行時的類型信息,這就產(chǎn)生了運行時類型識別的道運行時的類型信息,這就產(chǎn)生了運行時類型識別的要求。要求。 運行時類型識別運行時類型識別 上

5、面是一個典型的類繼承關系圖,基類在上,派生上面是一個典型的類繼承關系圖,基類在上,派生類向下生長。面向?qū)ο蟪绦蛟O計的一般目標就是用代類向下生長。面向?qū)ο蟪绦蛟O計的一般目標就是用代碼管理指向基類的指針。所以如果想增加一個新類來碼管理指向基類的指針。所以如果想增加一個新類來擴充程序(比如從擴充程序(比如從shape中派生出中派生出rhomboid),代碼體),代碼體部分并不受影響。部分并不受影響。 shape circle square triangle運行時類型識別運行時類型識別 在上例中,在上例中,shape接口部分的虛函數(shù)是接口部分的虛函數(shù)是draw(),其目其目的就是讓用戶通過一個的就是讓

6、用戶通過一個shape指針來調(diào)用指針來調(diào)用draw(),draw()在所有的派生類中都被重新定義。由于它是一在所有的派生類中都被重新定義。由于它是一個虛函數(shù),所以即使是用一個個虛函數(shù),所以即使是用一個shape()型的指針來調(diào)用型的指針來調(diào)用它,它仍然會被正確調(diào)用。創(chuàng)建一個特定的對象它,它仍然會被正確調(diào)用。創(chuàng)建一個特定的對象(circle、square、triangle),取其地址并把它映射),取其地址并把它映射到到shape*(忘掉對象的實際類型),然后在程序的其(忘掉對象的實際類型),然后在程序的其它地方使用這個匿名指針它地方使用這個匿名指針這種從多個派生類到基這種從多個派生類到基類的映射

7、叫做類的映射叫做向上映射向上映射。運行時類型識別運行時類型識別 假如在編程中遇到了特殊的需求,需要知道一假如在編程中遇到了特殊的需求,需要知道一個一般指針的準確類型,該怎么辦?個一般指針的準確類型,該怎么辦? 比如,假設允許我們的用戶將任一形狀變成紫比如,假設允許我們的用戶將任一形狀變成紫色來表示加亮。用這種方法,他們可以發(fā)現(xiàn)屏幕上的色來表示加亮。用這種方法,他們可以發(fā)現(xiàn)屏幕上的所有三角形都被加亮。我們可能自然地想到用虛函數(shù),所有三角形都被加亮。我們可能自然地想到用虛函數(shù),像像turncolorifyouarea ( ),它允許一些種類顏色的枚,它允許一些種類顏色的枚舉型參數(shù)和舉型參數(shù)和sha

8、pe:circle、shape:square或或shape:triangle參數(shù)。參數(shù)。 運行時類型識別運行時類型識別 為了解決這種問題,多數(shù)類庫設計者會把虛函為了解決這種問題,多數(shù)類庫設計者會把虛函數(shù)放在基類中,使運行時返回特定對象的類型信息。數(shù)放在基類中,使運行時返回特定對象的類型信息。我們可能見過一些名字為我們可能見過一些名字為isa( )和和typeof() 之類的成之類的成員函數(shù),這些就是開發(fā)商定義的員函數(shù),這些就是開發(fā)商定義的rtti函數(shù)。使用這函數(shù)。使用這些函數(shù),當處理一個對象列表時就可以說:些函數(shù),當處理一個對象列表時就可以說:“如果這如果這個對象是個對象是triangle類的

9、,就把它變成紫色。類的,就把它變成紫色。”運行時類型識別運行時類型識別9使用使用typeid 運算符的前提:必須有運算符的前提:必須有typeinfo類的支持。類的支持。該類為所有的內(nèi)置類型和多態(tài)類型的對象保存了運行時類型信該類為所有的內(nèi)置類型和多態(tài)類型的對象保存了運行時類型信息。它息。它在頭文件在頭文件 中定義的。中定義的。常用該類的常用該類的四個成員函數(shù)四個成員函數(shù):測試兩個對象的類型是否相同測試兩個對象的類型是否相同: bool operator =(const typeinfo &ob)const; bool operator !=(const typeinfo &ob

10、)const; 返回被測對象的類型名返回被測對象的類型名: const char * name()const;判斷兩個對象定義的前后關系:判斷兩個對象定義的前后關系: bool before(const typeinfo & ob)const;typeinfo類類運行時類型識別運行時類型識別10 typeid(對象名對象名/類型名類型名) : 返回一個返回一個typeinfo類的對象類的對象,記錄著目標類的類型。記錄著目標類的類型。 static_cast(源對象源對象):將對象靜態(tài)轉(zhuǎn)換為目將對象靜態(tài)轉(zhuǎn)換為目標類型標類型。 dynamic_cast(源對象源對象):若源對象若源對象與與

11、目標類目標類型型存在存在 is - a 關系,則完成轉(zhuǎn)換,否則失敗。關系,則完成轉(zhuǎn)換,否則失敗。 將父將父類的指針變?yōu)樽宇惖闹羔?。類的指針變?yōu)樽宇惖闹羔?。所涉及的運算符所涉及的運算符 運行時類型識別運行時類型識別 2. rtti的兩種使用方法的兩種使用方法: 使用使用rtti有兩種方法。第一種就像有兩種方法。第一種就像sizeof(),它看上就像一,它看上就像一個函數(shù)。但實際上它是由編譯器實現(xiàn)的。個函數(shù)。但實際上它是由編譯器實現(xiàn)的。typeid()帶有一個參數(shù),帶有一個參數(shù),它可以是一個對象引用或指針,返回全局它可以是一個對象引用或指針,返回全局typeinfo類的常量對象類的常量對象的一個引

12、用??梢杂眠\算符的一個引用。可以用運算符“= =”和和“!=”來互相比較這些對象。來互相比較這些對象。也可以用也可以用name()來獲得類型的名稱。注意,如果給來獲得類型的名稱。注意,如果給typeid()傳遞傳遞一個一個shape*型參數(shù),它會認為類型為型參數(shù),它會認為類型為shape*,所以如果想知道,所以如果想知道一個指針所指對象的精確類型,我們必須逆向引用這個指針。一個指針所指對象的精確類型,我們必須逆向引用這個指針。比如,比如,s是個是個shape* ,那么:那么: cout typeid(*s).name()endl; 將顯示出將顯示出s所指向的對象類型。所指向的對象類型。運行時類

13、型識別運行時類型識別 為了保持一致性,為了保持一致性,typeid()也可以用于內(nèi)部類型,也可以用于內(nèi)部類型,所以下面的表達式結果為所以下面的表達式結果為true: typeid(47) = typeid(int) typeid(0) = typeid(int) int i; typeid(i) = typeid(int) typeid(&i) =typeid(int*)運行時類型識別運行時類型識別13可以用可以用typeid 檢查基本類型和非多態(tài)類型:檢查基本類型和非多態(tài)類型: #include #include #include using namespace std;typede

14、f unsigned int uint ;void func() cout typeid(uint).name()endl; cout typeid(string).name()endl;顯示:顯示:“unsigned int”“string”運行時類型識別運行時類型識別 rtti的第二個用法叫的第二個用法叫“安全類型向下映射安全類型向下映射”。之所以用之所以用“向下映射向下映射”這個詞也是由于類繼承的排列這個詞也是由于類繼承的排列順序。如果映射一個順序。如果映射一個circle*到到shape*叫叫向上映射向上映射的話,的話,那么將一個那么將一個shape*映射成一個映射成一個circle*

15、就叫就叫向下映射向下映射了。了。當然一個當然一個circle*也是一個也是一個shape*,編譯器允許任意的,編譯器允許任意的向上映射,但一個向上映射,但一個shape*不一定就是不一定就是circle*,所以編,所以編譯器在沒有明確的類型映射時并不允許我們完成一個譯器在沒有明確的類型映射時并不允許我們完成一個向下映射任務。向下映射任務。運行時類型識別運行時類型識別 向下映射的一般方法是:創(chuàng)建一個函數(shù)來試著將向下映射的一般方法是:創(chuàng)建一個函數(shù)來試著將shape*指派為一個指派為一個circle * (在本例中在本例中),檢查執(zhí)行過程中的,檢查執(zhí)行過程中的數(shù)據(jù)類型。如果這個函數(shù)返回一個非空地址,

16、則成功;如數(shù)據(jù)類型。如果這個函數(shù)返回一個非空地址,則成功;如果返回果返回null,說明我們并沒有一個,說明我們并沒有一個circle*對象。對象。 c+的的rtti的的“安全類型向下映射安全類型向下映射”就是按照這種就是按照這種“試探映射試探映射”函數(shù)的格式,但它(非常合理地)用模板語函數(shù)的格式,但它(非常合理地)用模板語法來產(chǎn)生這個特殊的動態(tài)映射函數(shù)(法來產(chǎn)生這個特殊的動態(tài)映射函數(shù)(dynamic_cast)所以)所以本例變成:本例變成: shape* sp=new circle; circle* cp=dynamic_cast(sp); if(cp) cout“cast successfu

17、l”;運行時類型識別運行時類型識別 如果想算出各種如果想算出各種shape的數(shù)目,可以使用下面的的數(shù)目,可以使用下面的框架:框架: circle* cp=dynamic_cast(sh) square* sp=dynamic_cast(sh) triangle* tp=dynamic_cast(sh) 當然這是方法之一。我們還可以在各個類型中放當然這是方法之一。我們還可以在各個類型中放置一個靜態(tài)數(shù)據(jù)成員,并在構造函數(shù)中對它自增計置一個靜態(tài)數(shù)據(jù)成員,并在構造函數(shù)中對它自增計數(shù)。這樣我們可以使用靜態(tài)數(shù)據(jù)成員和動態(tài)映射兩數(shù)。這樣我們可以使用靜態(tài)數(shù)據(jù)成員和動態(tài)映射兩種方法結合起來計算種方法結合起來計算

18、shape的個數(shù)。的個數(shù)。運行時類型識別運行時類型識別 rtti允許我們用一個匿名的多態(tài)指針來發(fā)現(xiàn)類型允許我們用一個匿名的多態(tài)指針來發(fā)現(xiàn)類型信息,所以它常常被初學者濫用,因為它可能在虛函信息,所以它常常被初學者濫用,因為它可能在虛函數(shù)完成之前就有意義了。數(shù)完成之前就有意義了。 對于許多有過程編程背景的人來說,要他們不把對于許多有過程編程背景的人來說,要他們不把程序組織成為一組程序組織成為一組switch語句是非常困難的。他們可語句是非常困難的。他們可能會用能會用rtti完成這些,但這樣會在代碼開發(fā)維護階段完成這些,但這樣會在代碼開發(fā)維護階段丟失多態(tài)性的非常重要的價值。丟失多態(tài)性的非常重要的價值

19、。 c+的意圖是:盡可能地使用虛函數(shù),必要時才的意圖是:盡可能地使用虛函數(shù),必要時才使用使用rtti。運行時類型識別運行時類型識別 當然,要想充分使用虛函數(shù),我們必須控制基當然,要想充分使用虛函數(shù),我們必須控制基類的定義。但隨著程序的擴大,我們可能發(fā)現(xiàn)基類類的定義。但隨著程序的擴大,我們可能發(fā)現(xiàn)基類并沒有我們想要的虛函數(shù),如果基類來自類庫或其并沒有我們想要的虛函數(shù),如果基類來自類庫或其他由別人控制的來源,就可以用他由別人控制的來源,就可以用rtti作為一種解決作為一種解決辦法:我們可以繼承一個新類并加上我們的成員函辦法:我們可以繼承一個新類并加上我們的成員函數(shù)。在代碼的其他地方我們可以檢測到我

20、們的新增數(shù)。在代碼的其他地方我們可以檢測到我們的新增類型和調(diào)用的那個成員函數(shù)。這不會破壞多態(tài)性和類型和調(diào)用的那個成員函數(shù)。這不會破壞多態(tài)性和程序邏輯的可擴展性。程序邏輯的可擴展性。運行時類型識別運行時類型識別 rtti有時可以解決效率問題。如果代碼用有時可以解決效率問題。如果代碼用一種好的方法使用多態(tài)機制,但結果是這種通一種好的方法使用多態(tài)機制,但結果是這種通用代碼對某個對象起反作用,使其運行效率低用代碼對某個對象起反作用,使其運行效率低下。我們可以用下。我們可以用rtti將這種類型找出來,并將這種類型找出來,并寫出針對特定情況的代碼以提高效率。寫出針對特定情況的代碼以提高效率。運行時類型識別

21、運行時類型識別20dynamic_cast運算符可完成兩個方向的轉(zhuǎn)換:運算符可完成兩個方向的轉(zhuǎn)換: upcast : 能將派生類的指針、引用轉(zhuǎn)換成基類類能將派生類的指針、引用轉(zhuǎn)換成基類類型。(可以用類型兼容規(guī)則隱含的進行)型。(可以用類型兼容規(guī)則隱含的進行) downcast :將基類的指針、引用轉(zhuǎn)換成派生類:將基類的指針、引用轉(zhuǎn)換成派生類類型。如果確屬同一類族,且是公有派生,則轉(zhuǎn)換成類型。如果確屬同一類族,且是公有派生,則轉(zhuǎn)換成功,否則失敗,失敗將拋出系統(tǒng)的功,否則失敗,失敗將拋出系統(tǒng)的bad_cast類型異類型異常。常。注意:注意: dynamic_cast只可轉(zhuǎn)換指針或引用,不可只可轉(zhuǎn)換

22、指針或引用,不可作用于對象。作用于對象。dynamic_cast運算符功能運算符功能 運行時類型識別運行時類型識別21 為了支持dynamic_cast運算符,系統(tǒng)必須維護一棵繼承樹,即base class table 模型。通過遍歷該繼承樹來確定被轉(zhuǎn)換對象和目標類型間是否存在 is-a 關系。這種“模糊匹配”的能力系統(tǒng)為之付出了時空代價,也正是rtti的魅力所在。而typeid和虛函數(shù)則屬“精確匹配”,無需額外開銷。dynamic_cast的機制的機制運行時類型識別運行時類型識別22先激活先激活rtti;對象所屬類型必須是多態(tài)類族;對象所屬類型必須是多態(tài)類族;若使用若使用dynamic_cast轉(zhuǎn)換一個引用,則要使用異轉(zhuǎn)換一個引用

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論