如何在執行時確定物件型別(RTTI)
RTTI 是“Runtime Type Information”的縮寫,意思是:執行時型別資訊。它提供了執行時確定物件型別的方法。本文將簡略介紹 RTTI 的一些背景知識、描述 RTTI 的概念,並通過具體例子和程式碼介紹什麼時候使用以及如何使用 RTTI;本文還將詳細描述兩個重要的 RTTI 運算子的使用方法,它們是 typeid 和 dynamic_cast。
其實,RTTI 在C++中並不是什麼新的東西,它早在十多年以前就已經出現了。但是大多數開發人員,包括許多高層次的C++程式設計師對它並不怎麼熟悉,更不用說使用 RTTI 來設計和編寫應用程式了。
一些物件導向專家在傳播自己的設計理念時,大多都主張在設計和開發中明智地使用虛擬成員函式,而不用 RTTI 機制。但是,在很多情況下,虛擬函式無法克服本身的侷限。每每涉及到處理異類容器和根基類層次(如 MFC)時,不可避免要對物件型別進行動態判斷,也就是動態型別的偵測。如何確定物件的動態型別呢?答案是使用內建的 RTTI 中的運算子:typeid 和 dynamic_cast。
首先讓我們來設計一個類層次,假設我們建立了某個處理檔案的抽象基類。它宣告下列純虛擬函式:open()、close()、read()和 write():
class File { public: virtual int open(const string & filename)=0; virtual int close(const string & filename)=0; // virtual ~File()=0; // 記住新增純虛擬解構函式(dtor) };
現在從 File 類派生的類要實現基類的純虛擬函式,同時還要提供一些其他的操作。假設派生類為 DiskFile,除了實現基類的純虛擬函式外,還要實現自己的flush()和defragment()操作:
class DiskFile: public File { public: int open(const string & filename); // 實現其他的純虛擬函式 ...... // 自己的專有操作 virtual int flush(); virtual int defragment(); };
接著,又從 DiskFile 類派生兩個類,假設為 TextFile 和 MediaFile。前者針對文字檔案,後者針對音訊和視訊檔案:
class TextFile: public DiskFile { // ...... int sort_by_words(); }; class MediaFile: public DiskFile { //...... };
我們之所以要建立這樣的類層次,是因為這樣做以後可以建立多型物件,如:
File *pfile; // *pfile的靜態型別是 File if(some_condition) pfile = new TextFile; // 動態型別是 TextFile else pfile = new DiskFile; // 動態型別是 DiskFile
假設你正在開發一個基於圖形使用者介面(GUI)的檔案管理器,每個檔案都可以以圖示方式顯示。當滑鼠移到圖示上並單擊右鍵時,檔案管理器開啟一個選單,每個檔案除了共同的選單項,不同的檔案型別還有不同的選單項。如:共同的選單項有“開啟”“拷貝”、和“貼上”,此外,還有一些針對特殊檔案的專門操作。比如,文字檔案會有“編輯”操作,而多媒體檔案則會有“播放”選單。為了使用 RTTI 來動態定製選單,檔案管理器必須偵測每個檔案的動態型別。利用 運算子 typeid 可以獲取與某個物件關聯的執行時型別資訊。typeid 有一個引數,傳遞物件或型別名。因此,為了確定 x 的動態型別是不是Y,可以用表示式:typeid(x) == typeid(Y)實現:
#include <typeinfo> // typeid 需要的標頭檔案 void menu::build(const File * pfile) { if (typeid(*pfile)==typeid(TextFile)) { add_option("edit"); } else if (typeid(*pfile)==typeid(MediaFile)) { add_option("play"); } }
使用 typeid 要注意一個問題,那就是某些編譯器(如 Visual C++)預設狀態是禁用 RTTI 的,目的是消除效能上的開銷。如果你的程式確實使用了 RTTI,一定要記住在編譯前啟用 RTTI。使用 typeid 可能產生一些將來的維護問題。假設你決定擴充套件上述的類層次,從MediaFile 派生另一個叫 LocalizeMedia 的類,用這個類表示帶有不同語言說明文字的媒體檔案。但 LocalizeMedia 本質上還是個 MediaFile 型別的檔案。因此,當使用者在該類檔案圖示上單擊右鍵時,檔案管理器必須提供一個“播放”選單。可惜 build()成員函式會呼叫失敗,原因是你沒有檢查這種特定的檔案型別。為了解決這個問題,你必須象下面這樣對 build() 打補丁:
void menu::build(const File * pfile) { //...... else if (typeid(*pfile)==typeid(LocalizedMedia)) { add_option("play"); } }
唉,這種做法真是顯得太業餘了,以後每次新增新的類,毫無疑問都必須打類似的補丁。顯然,這不是一個理想的解決方案。這個時候我們就要用到 dynamic_cast,這個運算子用於多型程式設計中保證在執行時發生正確的轉換(即編譯器無法驗證是否發生正確的轉換)。用它來確定某個物件是 MediaFile 物件還是它的派生類物件。dynamic_cast 常用於從多型程式設計基類指標向派生類指標的向下型別轉換。它有兩個引數:一個是型別名;另一個是多型物件的指標或引用。其功能是在執行時將物件強制轉換為目標型別並返回布林型結果。也就是說,如果該函式成功地並且是動態的將 *pfile 強制轉換為 MediaFile,那麼 pfile的動態型別是 MediaFile 或者是它的派生類。否則,pfile 則為其它的型別:
void menu::build(const File * pfile) { if (dynamic_cast <MediaFile *> (pfile)) { // pfile 是 MediaFile 或者是MediaFile的派生類 LocalizedMedia add_option("play"); } else if (dynamic_cast <TextFile*> (pfile)) { // pfile 是 TextFile 是TextFile的派生類 add_option("edit"); } }
細細想一下,雖然使用 dynamic_cast 確實很好地解決了我們的問題,但也需要我們付出代價,那就是與 typeid 相比,dynamic_cast 不是一個常量時間的操作。為了確定是否能完成強制型別轉換,dynamic_cast`必須在執行時進行一些轉換細節操作。因此在使用 dynamic_cast 操作時,應該權衡對效能的影響。
相關文章
- Java中執行時型別識別RTTIJava型別
- C 語言Struct 實現執行型別識別 RTTIStruct型別
- 確定enqueue鎖型別ENQ型別
- 將不確定變為確定~類中的屬性何時被執行
- 定義SQL*PLUS型別的可執行SQL型別
- 時間物件、引用型別物件型別
- 引數如何在python中明確型別?Python型別
- [CLR via C#]4. 型別基礎及型別、物件、棧和堆執行時的相互聯絡C#型別物件
- int型別和long long型別運算執行時間的差別型別
- MySQL定時執行MySql
- 物件型介面 / 定製操作型別和欄位物件型別
- LoadRunner如何在指令碼執行時修改log設定選項?指令碼
- PHP定時執行任務PHP
- OC的多型(執行時)轉多型
- 如何在Typescript中定義Promise的返回值型別TypeScriptPromise型別
- 多執行緒-獲取和設定執行緒物件名稱執行緒物件
- 對於返回值型別不確定的函式如何限定返回值型別型別函式
- ios 多執行緒定時器iOS執行緒定時器
- spring執行定時任務Spring
- Java如何停止執行緒,確定你知道的都是正確的麼?Java執行緒
- RTTI
- swift值型別的執行緒安全Swift型別執行緒
- 執行迴路RunLoop型別機制OOP型別
- JavaScript筆記5:計時器、物件、基本資料型別、引用資料型別JavaScript筆記物件資料型別
- SAP LE 交貨單裡的移動型別的確定型別
- Objective-C的物件模型與執行時Object物件模型
- ORACLE物件型別表Oracle物件型別
- 【java】【多執行緒】獲取和設定執行緒名字、獲取執行緒物件(3)Java執行緒物件
- Linux 定時執行指令碼、命令Linux指令碼
- 通過 Redis 定時執行指令碼Redis指令碼
- php後臺定時執行任務PHP
- 馴服定時器和執行緒定時器執行緒
- MySQL cron定時執行SQL語句MySql
- Linux定時執行.sh指令碼Linux指令碼
- Debian的定時執行命令Crontab
- java web每天定時執行任務JavaWeb
- 採用job定時執行recover datafile
- 設計表時,如何選擇正確的資料型別資料型別