轉(zhuǎn)帖|其它|編輯:郝浩|2010-08-19 10:56:33.000|閱讀 504 次
概述:RTTI 是“Runtime Type Information”的縮寫(xiě),意思是:運(yùn)行時(shí)類(lèi)型信息。它提供了運(yùn)行時(shí)確定對(duì)象類(lèi)型的方法。本文將簡(jiǎn)略介紹 RTTI 的一些背景知識(shí)、描述 RTTI 的概念,并通過(guò)具體例子和代碼介紹什么時(shí)候使用以及如何使用 RTTI;本文還將詳細(xì)描述兩個(gè)重要的 RTTI 運(yùn)算符的使用方法,它們是 typeid 和 dynamic_cast。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門(mén)軟控件火熱銷(xiāo)售中 >>
RTTI 是“Runtime Type Information”的縮寫(xiě),意思是:運(yùn)行時(shí)類(lèi)型信息。它提供了運(yùn)行時(shí)確定對(duì)象類(lèi)型的方法。本文將簡(jiǎn)略介紹 RTTI 的一些背景知識(shí)、描述 RTTI 的概念,并通過(guò)具體例子和代碼介紹什么時(shí)候使用以及如何使用 RTTI;本文還將詳細(xì)描述兩個(gè)重要的 RTTI 運(yùn)算符的使用方法,它們是 typeid 和 dynamic_cast。
其實(shí),RTTI 在C++中并不是什么新的東西,它早在十多年以前就已經(jīng)出現(xiàn)了。但是大多數(shù)開(kāi)發(fā)人員,包括許多高層次的C++程序員對(duì)它并不怎么熟悉,更不用說(shuō)使用 RTTI 來(lái)設(shè)計(jì)和編寫(xiě)應(yīng)用程序了。
一些面向?qū)ο髮?zhuān)家在傳播自己的設(shè)計(jì)理念時(shí),大多都主張?jiān)谠O(shè)計(jì)和開(kāi)發(fā)中明智地使用虛擬成員函數(shù),而不用 RTTI 機(jī)制。但是,在很多情況下,虛擬函數(shù)無(wú)法克服本身的局限。每每涉及到處理異類(lèi)容器和根基類(lèi)層次(如 MFC)時(shí),不可避免要對(duì)對(duì)象類(lèi)型進(jìn)行動(dòng)態(tài)判斷,也就是動(dòng)態(tài)類(lèi)型的偵測(cè)。如何確定對(duì)象的動(dòng)態(tài)類(lèi)型呢?答案是使用內(nèi)建的 RTTI 中的運(yùn)算符:typeid 和 dynamic_cast。
首先讓我們來(lái)設(shè)計(jì)一個(gè)類(lèi)層次,假設(shè)我們創(chuàng)建了某個(gè)處理文件的抽象基類(lèi)。它聲明下列純虛擬函數(shù):open()、close()、read()和 write():
class File
{
public:
virtual int open(const string & filename)=0;
virtual int close(const string & filename)=0;
//
virtual ~File()=0; // 記住添加純虛擬析構(gòu)函數(shù)(dtor)
};
現(xiàn)在從 File 類(lèi)派生的類(lèi)要實(shí)現(xiàn)基類(lèi)的純虛擬函數(shù),同時(shí)還要提供一些其他的操作。假設(shè)派生類(lèi)為 DiskFile,除了實(shí)現(xiàn)基類(lèi)的純虛擬函數(shù)外,還要實(shí)現(xiàn)自己的flush()和defragment()操作:
class DiskFile: public File
{
public:
int open(const string & filename);
// 實(shí)現(xiàn)其他的純虛擬函數(shù)
......
// 自己的專(zhuān)有操作
virtual int flush();
virtual int defragment();
};
接著,又從 DiskFile 類(lèi)派生兩個(gè)類(lèi),假設(shè)為 TextFile 和 MediaFile。前者針對(duì)文本文件,后者針對(duì)音頻和視頻文件:
class TextFile: public DiskFile
{
// ......
int sort_by_words();
};
class MediaFile: public DiskFile
{
//......
};
我們之所以要?jiǎng)?chuàng)建這樣的類(lèi)層次,是因?yàn)檫@樣做以后可以創(chuàng)建多態(tài)對(duì)象,如:
File *pfile; // *pfile的靜態(tài)類(lèi)型是 File
if(some_condition)
pfile = new TextFile; // 動(dòng)態(tài)類(lèi)型是 TextFile
else
pfile = new DiskFile; // 動(dòng)態(tài)類(lèi)型是 DiskFile
假設(shè)你正在開(kāi)發(fā)一個(gè)基于圖形用戶(hù)界面(GUI)的文件管理器,每個(gè)文件都可以以圖標(biāo)方式顯示。當(dāng)鼠標(biāo)移到圖標(biāo)上并單擊右鍵時(shí),文件管理器打開(kāi)一個(gè)菜單,每個(gè)文件除了共同的菜單項(xiàng),不同的文件類(lèi)型還有不同的菜單項(xiàng)。如:共同的菜單項(xiàng)有“打開(kāi)”“拷貝”、和“粘貼”,此外,還有一些針對(duì)特殊文件的專(zhuān)門(mén)操作。比如,文本文件會(huì)有“編輯”操作,而多媒體文件則會(huì)有“播放”菜單。為了使用 RTTI 來(lái)動(dòng)態(tài)定制菜單,文件管理器必須偵測(cè)每個(gè)文件的動(dòng)態(tài)類(lèi)型。利用 運(yùn)算符 typeid 可以獲取與某個(gè)對(duì)象關(guān)聯(lián)的運(yùn)行時(shí)類(lèi)型信息。typeid 有一個(gè)參數(shù),傳遞對(duì)象或類(lèi)型名。因此,為了確定 x 的動(dòng)態(tài)類(lèi)型是不是Y,可以用表達(dá)式:typeid(x) == typeid(Y)實(shí)現(xiàn):
#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 要注意一個(gè)問(wèn)題,那就是某些編譯器(如 Visual C++)默認(rèn)狀態(tài)是禁用 RTTI 的,目的是消除性能上的開(kāi)銷(xiāo)。如果你的程序確實(shí)使用了 RTTI,一定要記住在編譯前啟用 RTTI。使用 typeid 可能產(chǎn)生一些將來(lái)的維護(hù)問(wèn)題。假設(shè)你決定擴(kuò)展上述的類(lèi)層次,從MediaFile 派生另一個(gè)叫 LocalizeMedia 的類(lèi),用這個(gè)類(lèi)表示帶有不同語(yǔ)言說(shuō)明文字的媒體文件。但 LocalizeMedia 本質(zhì)上還是個(gè) MediaFile 類(lèi)型的文件。因此,當(dāng)用戶(hù)在該類(lèi)文件圖標(biāo)上單擊右鍵時(shí),文件管理器必須提供一個(gè)“播放”菜單。可惜 build()成員函數(shù)會(huì)調(diào)用失敗,原因是你沒(méi)有檢查這種特定的文件類(lèi)型。為了解決這個(gè)問(wèn)題,你必須象下面這樣對(duì) build() 打補(bǔ)丁:
void menu::build(const File * pfile)
{
//......
else if (typeid(*pfile)==typeid(LocalizedMedia))
{
add_option("play");
}
}
唉,這種做法真是顯得太業(yè)余了,以后每次添加新的類(lèi),毫無(wú)疑問(wèn)都必須打類(lèi)似的補(bǔ)丁。顯然,這不是一個(gè)理想的解決方案。這個(gè)時(shí)候我們就要用到 dynamic_cast,這個(gè)運(yùn)算符用于多態(tài)編程中保證在運(yùn)行時(shí)發(fā)生正確的轉(zhuǎn)換(即編譯器無(wú)法驗(yàn)證是否發(fā)生正確的轉(zhuǎn)換)。用它來(lái)確定某個(gè)對(duì)象是 MediaFile 對(duì)象還是它的派生類(lèi)對(duì)象。dynamic_cast 常用于從多態(tài)編程基類(lèi)指針向派生類(lèi)指針的向下類(lèi)型轉(zhuǎn)換。它有兩個(gè)參數(shù):一個(gè)是類(lèi)型名;另一個(gè)是多態(tài)對(duì)象的指針或引用。其功能是在運(yùn)行時(shí)將對(duì)象強(qiáng)制轉(zhuǎn)換為目標(biāo)類(lèi)型并返回布爾型結(jié)果。也就是說(shuō),如果該函數(shù)成功地并且是動(dòng)態(tài)的將 *pfile 強(qiáng)制轉(zhuǎn)換為 MediaFile,那么 pfile的動(dòng)態(tài)類(lèi)型是 MediaFile 或者是它的派生類(lèi)。否則,pfile 則為其它的類(lèi)型:
void menu::build(const File * pfile)
{
if (dynamic_cast <MediaFile *> (pfile))
{
// pfile 是 MediaFile 或者是MediaFile的派生類(lèi) LocalizedMedia
add_option("play");
}
else if (dynamic_cast <TextFile*> (pfile))
{
// pfile 是 TextFile 是TextFile的派生類(lèi)
add_option("edit");
}
}
細(xì)細(xì)想一下,雖然使用 dynamic_cast 確實(shí)很好地解決了我們的問(wèn)題,但也需要我們付出代價(jià),那就是與 typeid 相比,dynamic_cast 不是一個(gè)常量時(shí)間的操作。為了確定是否能完成強(qiáng)制類(lèi)型轉(zhuǎn)換,dynamic_cast`必須在運(yùn)行時(shí)進(jìn)行一些轉(zhuǎn)換細(xì)節(jié)操作。因此在使用 dynamic_cast 操作時(shí),應(yīng)該權(quán)衡對(duì)性能的影響。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:網(wǎng)絡(luò)轉(zhuǎn)載