如何在运行时确定对象类型(RTTI)
生活随笔
收集整理的這篇文章主要介紹了
如何在运行时确定对象类型(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 elsepfile = 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的派生類 LocalizedMediaadd_option("play"); }else if (dynamic_cast <TextFile*> (pfile)){// pfile 是 TextFile 是TextFile的派生類add_option("edit"); } }
??? 其實,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 elsepfile = 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的派生類 LocalizedMediaadd_option("play"); }else if (dynamic_cast <TextFile*> (pfile)){// pfile 是 TextFile 是TextFile的派生類add_option("edit"); } }
??? 細細想一下,雖然使用 dynamic_cast 確實很好地解決了我們的問題,但也需要我們付出代價,那就是與 typeid 相比,dynamic_cast 不是一個常量時間的操作。為了確定是否能完成強制類型轉換,dynamic_cast`必須在運行時進行一些轉換細節操作。因此在使用 dynamic_cast 操作時,應該權衡對性能的影響。
作者:NorthTibet
轉自:http://www.vckbase.com/document/viewdoc/?id=653
轉載于:https://www.cnblogs.com/stli/archive/2010/10/09/1846906.html
總結
以上是生活随笔為你收集整理的如何在运行时确定对象类型(RTTI)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#中窗体的close,dispose,
- 下一篇: 基于Web用户控件的Portal