您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“C++的RTTI和cast運算符如何使用”,內容詳細,步驟清晰,細節處理妥當,希望這篇“C++的RTTI和cast運算符如何使用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
RTTI是運行階段類型識別(Running Type Identificarion)的簡稱。
如何知道指針指向的是哪種對象?
這是個很常見的問題,由于我們允許使用基類指針指向派生類,所以基類指針指向的對象可能是基類對象,也可能是派生類對象。但是我們需要知道對象種類,因為我們需要使用正確的類方法。
RTTI能解決上述問題。
dynamic_cast
是最常用的RTTI組件,它不能回答"指針指向的是哪類對象",但是它能回答"是否可以安全的將對象的地址賦給特定類型指針"。
什么是安全?
例如:A是基類,B是A的派生類,C是B的派生類。那么將BC對象的地址賦給A指針是安全的,而反向就是不安全的。因為公有繼承滿足is-a
關系,指針和引用進行向上轉換是安全的,向下轉換就是不安全的。
dynamic_cast
的用法是:
dynamic_cast<Type*>(ptr)
或dynamic_cast<Type&>(ref)
可以看到的是,dynamic_cast
是一種類型轉換符,它支持指針間的轉換,他將ptr
的類型轉換成Type*
,如果這種轉換是安全的,那會返回轉換后的指針;如果不安全那就會返回空指針。對于引用的話,他將ref
的類型轉換成Type&
,如果這種轉換是安全的,那就返回轉換后的引用;如果不安全那就會引發bad_cast
異常。
總之,dynamic_cast
類型轉換運算符比C風格的類型轉換要安全的多,而且它能夠判斷轉換是否安全。
//RTTI1.cpp #include<iostream> #include<cstdlib> #include<ctime> using namespace std; class Grand { protected: int hold; public: Grand(int h=0):hold(h){}; virtual void Speak() const {cout<<"I am Grand class!\n";} }; class Superb:public Grand { public: Superb(int h=0):Grand(h){} virtual void Speak() const {cout<<"I am a superb class!\n";} virtual void Say() const {cout<<"the value: "<<Grand::hold<<endl;} }; class Magnificent:public Superb { private: char ch; public: Magnificent(int h=0,char c='A'):Superb(h),ch(c){} virtual void Speak() const{cout<<"I am a Magnificent class!\n";} virtual void Say() const{cout<<"the character: "<<ch<<endl<<"the value: "<<Grand::hold<<endl;} }; Grand *GetOne(); int main() { srand(time(0)); Grand *pg; Superb *ps; for(int i=0;i<5;i++) { pg=GetOne(); pg->Speak(); if(ps=dynamic_cast<Superb*>(pg)) { ps->Say(); } } delete pg; return 0; } Grand * GetOne() { Grand *p; switch (rand()%3) { case 0: p=new Grand(rand()%100); break; case 1: p=new Superb(rand()%100); break; case 2: p=new Magnificent(rand()%100,'A'+rand()%26); break; } return p; }
I am a Magnificent class!
the character: I
the value: 74
I am a superb class!
the value: 50
I am Grand class!
I am Grand class!
I am a Magnificent class!
the character: V
the value: 99
上面這個例子說明了重要的一點:盡可能使用虛函數,而只在必要時使用RTTI。
如果將dynamic_cast
用于引用,當請求不安全的時候,會引發bad_cast
異常,這種異常時從exception
類派生而來的,它在頭文件typeinfo
中定義。
則我們可以使用如下方式處理異常:
#include<typeinfo> ... try{ Superb & rs= dynamic_cast<Superb &>(rg); .... } catch(bad_cast &) { ... };
type_info
類是在頭文件typeinfo
中定義的一個類。type_info
類重載了==
和!=
運算符。
typeid
是一個運算符,它返回一個對type_info
的引用,它可以接受2種參數:類名和對象。typeid
是真正回答了"指針指向的是哪類對象"。
(實際上,typeid
也可以用于其他類型,例如結構體,函數指針,各種類型,只要sizeof
能接受的,typeid
都能接受)
用法:
typeid(Magnificent)==typeid(*pg)
如果pg
是一個空指針,則會引發bad_typeid
異常。
type_info
類中包含一個name()
接口,該接口返回字符串,一般情況下是類的名稱。
cout<<typeid(*pg).name()<<".\n"
例子:
//RTTI2.cpp #include<iostream> #include<cstdlib> #include<ctime> #include<typeinfo> using namespace std; class Grand { protected: int hold; public: Grand(int h=0):hold(h){}; virtual void Speak() const {cout<<"I am Grand class!\n";} }; class Superb:public Grand { public: Superb(int h=0):Grand(h){} virtual void Speak() const {cout<<"I am a superb class!\n";} virtual void Say() const {cout<<"the value: "<<Grand::hold<<endl;} }; class Magnificent:public Superb { private: char ch; public: Magnificent(int h=0,char c='A'):Superb(h),ch(c){} virtual void Speak() const{cout<<"I am a Magnificent class!\n";} virtual void Say() const{cout<<"the character: "<<ch<<endl<<"the value: "<<Grand::hold<<endl;} }; Grand *GetOne(); int main() { srand(time(0)); Grand *pg; Superb *ps; for(int i=0;i<5;i++) { pg=GetOne(); cout<<"Now processing type "<<typeid(*pg).name()<<".\n"; pg->Speak(); if(ps=dynamic_cast<Superb*>(pg)) { ps->Say(); } if(typeid(Magnificent)==typeid(*pg)) { cout<<"Yes,you are really magnificent.\n"; } } delete pg; return 0; } Grand * GetOne() { Grand *p; switch (rand()%3) { case 0: p=new Grand(rand()%100); break; case 1: p=new Superb(rand()%100); break; case 2: p=new Magnificent(rand()%100,'A'+rand()%26); break; } return p; }
Now processing type 6Superb.
I am a superb class!
the value: 64
Now processing type 11Magnificent.
I am a Magnificent class!
the character: Y
the value: 30
Yes,you are really magnificent.
Now processing type 5Grand.
I am Grand class!
Now processing type 11Magnificent.
I am a Magnificent class!
the character: C
the value: 3
Yes,you are really magnificent.
Now processing type 5Grand.
I am Grand class!
注意,你可能發現了typeid
遠比dynamic_cast
優秀的多,typeid
會直接告訴你類型是什么,而dynamic_cast
只告訴你是否可以類型轉換。但是typeid
的效率比dynamic_cast
低很多,所以,請優先考慮dynamic_cast
和虛函數,如果不行才使用typeid
。
C語言中的類型轉換符太過隨意,C++創始人認為,我們應該使用嚴格的類型轉換,則C++提供了4個類型轉換運算符:
dynamic_cast
const_cast
static_cast
reinterpret_cast
dynamic_cast
運算符已經介紹過了,它的用途是,使得類層次結構中進行向上轉換,而不允許其他轉換。
const_cast
運算符,用于除去或增加 類型的const
或volatile
屬性。
它的語法是:
const_cast<type-name>(expression)
如果類型的其他方面也被修改,則上述類型轉換就會出錯,也就是說,除了cv限定符
可以不同外,type_name
和expression
的類型必須相同。
提供該運算符的目的是:有時候我們需要一個值:它在大多數情況下是常量,而有時候我們需要更改它。
看一個有趣的例子:
//cast運算符1.cpp //cast運算符1.cpp #include <iostream> int main() { using std::cout; using std::endl; volatile const int a=100; volatile const int & ra=a;//無法通過ra修改a int &change_a=const_cast<int &>(ra);//可以通過change_a修改a; change_a=255; cout<<a<<endl; }
上面最后這個a
常量被修改成255,這是我們預期的結果。但是如果我把volatile
關鍵詞去掉,那么a
的值還是100。其實是編譯器在這里玩了個小聰明,const int a=100
,編譯器認為a就不會發生改變了,所以就把a放在了寄存器中,因為訪問寄存器要比訪問內存單元快的多,每次都從寄存器中取數據,但是后來a在內存中發生變化后,寄存器中的數據沒有發生變化,所以打印的是寄存器中的數據。解決這一問題,就使用volatile
關鍵詞,那么對a的存取都是直接對內存做操作的。
static_cast
運算符
它的語法是:
static_cast<type-name>(expression)
僅當type-name
可被隱式轉換成expression
所屬的類型或者expression
可以隱式轉換成type-name
類型時,上述轉換才合法,否則出現bad_cast
異常。
例如,枚舉值可以隱式轉換成整型,那么整型就可以通過static_cast
轉換成枚舉型。
總之,static_cast
只允許"相似"的類型間的轉換,而不允許"差異很大"的類間的轉換。
reinterpret_cast
運算符:重新詮釋類型,進行瘋狂的類型轉換
它的語法時:
reinterpret_cast<type-name>(expression)
它可以進行類型間"不可思議"的互換,但是它不允許刪除const
,也不允許進行喪失數據的轉換
例如下面這兩個例子:
#include<iostream> int main() { using namespace std; struct dat {short a; short b;}; long value =0xA224B118; dat *pd=reinterpret_cast<dat*>(&value); cout<<hex<<pd->a; }
#include<iostream> void fun() { std::cout<<"hello world!\n"; } int main() { using namespace std; void (*foo)(); foo=fun; int* p=reinterpret_cast<int*>(foo); }
通常,reinterpret_cast
轉換符適用于底層編程技術,是不可移植的,因為不同系統可能使用不同大小,不同順序來存儲同一類型的數據。
總之,C語言類型轉換比reinterpret_cast
還要"瘋狂",非常不安全,所以推薦使用cast
運算符來實現類型轉換。
讀到這里,這篇“C++的RTTI和cast運算符如何使用”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。