RTTI全名叫run time type identification,在運作時進行類型檢查/辨認,他能完成一些虛函數解決不了的問題
其應用場景舉例:
假設父類A定義了一些虛函數作為藍圖,衆多子類B、C、D、E·····都繼承了A,父類中的虛函數不可能囊括子類的所有行為,這些子類總會有一些獨有的派生行為,如果我們想要用父類指針指向衆多子類的衆多對象,打算用這個父類指針周遊這些對象,那麼我們用這個父類指針隻能調到衆多子類對象中共有的成員/方法,因為我們我們的周遊過程使用的是父類A指針,當我們想調用B、C、D、E等的獨有派生成員/方法,怎麼辦呢?當然是把A指針分别轉為B、C、D、E···的指針,那麼問題來了,我們怎麼知道要把A指針轉為B指針、還是C指針、還是D指針··呢?B、C、D、E··的對象傳入時是用的A指針,這沒問題,但是傳進來以後再轉回原先的類型就難了。
RTTI就用于解決這一問題,靠的是兩個運算符:typeid和dynamic_cast<>。
有了這兩個運算符,上述問題就解決了:
(1)使用typeid來解決,舉例
B b1,b2,b3;
C c1,c2;
D d1,d2;
A *pA[20];
pA[0]=&b1; pA[1]=&b2; pA[2]=&b3;
pA[3]=&c1; pA[4]=&c2;
pA[5]=&d1; pA[6]=&d2;
for(int i=0;i<7;i++)
{
if( typeid(*pA[i]) == typeid(B) )
{
B *pb = static_cast<B*>(pA[i]);
do_some_B(pb);//做一些B類獨有的行為
}
if( typeid(*pA[i]) == typeid(C) )
{
C *pC = static_cast<C*>(pA[i]);
do_some_C(pC);//做一些C類獨有的行為
}
````//其他代碼
}
typeid的使用方法類似于sizeof,參數既可以是類型,也可以是執行個體。
由上述例子看出typeid雖然在一定程度上解決了上述問題,但它仍有不足,繼續以上問題:假設又有一個類BB,繼承了類B,BB的對象顯然是擁有B的public方法的,如果在上述代碼基礎上增加一部分:
B b1,b2,b3;
C c1,c2;
D d1,d2;
BB bb1,bb2;
A *pA[20];
pA[0]=&b1; pA[1]=&b2; pA[2]=&b3;
pA[3]=&c1; pA[4]=&c2;
pA[5]=&d1; pA[6]=&d2;
pA[7]=&bb1; pA[8]=&bb2;
for(int i=0;i<9;i++)
{
if( typeid(*pA[i]) == typeid(B) )
{
B *pb = static_cast<B*>(pA[i]);
do_some_B(pb);//做一些B類獨有的行為
//BB的對象bb1、bb2的typeid是BB,不是B,故無法進入該if,但我們期望他們能進入該if,因為他們明明擁有B類的獨有行為
}
if( typeid(*pA[i]) == typeid(C) )
{
C *pC = static_cast<C*>(pA[i]);
do_some_C(pC);//做一些C類獨有的行為
}
if( typeid(*pA[i]) == typeid(BB) )
{//bb1、bb2會進入這個if,
C *pBB = static_cast<BB*>(pA[i]);
do_some_BB(pBB);//做一些BB類獨有的行為,但是因為BB有B的方法,do_some_BB的功能與上面第一條if的功能會存在重複
}
````//其他代碼
}
這裡注釋的比較清楚了,在上述應用情境中,使用typeid仍然不爽,對于這種情景,可以使用dynamic_cast<>運算符來解決,這個運算符在上一篇博文中已經記錄過了,這裡再次複習一下
(2)使用dynamic_cast<>運算符來解決,舉例
把上面代碼的第一條if語句改為:
if( NULL != dynamic_cast<B*>(pA[i]) )
{//B和BB的對象b1、b2、bb1、bb2都可以進入該if進行處理
B *pb = static_cast<B*>pA[i];
do_some_B(pb);//做一些B類獨有的行為,更近一步做一些BB類獨有的行為
}
或者改為:
try
{
if( B &rB = dynamic_cast<B&>(*pA[i]) )
{//B和BB的對象b1、b2、bb1、bb2都可以進入該if進行處理
do_some_B(rB);//做一些B類獨有的行為
}
}
catch(std::bad_cast&)//轉換失敗的異常
{
//彈出提示,或者跳過
}
這兩種方法都可以,不同的是,指針轉換失敗會傳回NULL,而引用轉換失敗會抛出異常,相比來說,還是檢查指針失敗較為簡潔,個人更傾向于第一種。