RTTI、dynamic_cast、typeid、虛函數表

一、RTTI

RTTI(Run-Time Type Identification),通過運行時類型信息,程序能夠使用基類指針或引用檢查這些指針或引用所指的對象的實際派生類

Human* phuman = new Man;

Human &a = *phuman;  // *phuman表示指針phuman所指向的對象

RTTI我們可以把這個程序看成是一種系統提供給我們的一種能力,或者一種功能。這種功能或者能力是通過2個運算符來體現:

(1)dynamic_cast運算符:能夠將將基類的指針或者引用安全的轉換爲派生類的指針或者引用

(2)typeid運算符:返回指針或者引用所指對象的實際類型

補充:想讓RTTI兩個運算符能夠過正常工作,基類中必須至少要有一個虛函數,不然這兩個運算符工作的結構就可能跟我們預期不一致。因爲只有虛函數的存在,這兩個運算符纔會使用指針或者引用所綁定的對象的動態類型。

二、dymamic_cast運算符

如果該運算符能夠轉換成功,說明這個指針實際上是要轉換到的那個類型。這個運算符能夠幫咱們做安全檢查

Human* phuman = new Men;
Men* p = (Men*)(phuman); // 用c語言風格的強制類型轉換,硬把指針轉換成Men*;
p->testfunc(); // 能夠正常的調用Men類的成員函數testfunc();

2.1指針

Human* phuman  = new Men;
Men* pmen = dynamic_cast<Men*>(phuman);
if (pemn != nullptr) {
    cout << "phuman實際是一個Men類型" << endl;
    // 在這裏操作類Men裏邊的成員函數,成員變量都能夠操作並且安全的操作
    pem->testfunc();
} else {
    // 轉換失敗
    out << "phuman不是一個Men類型" << endl;
}

2.2引用

如果用dynamic_cast轉換失敗,則系統會拋出一個std::bad_cast異常try{}catch(){}

Human* phuman  = new Men;
Human &q = *phuman; // 這就是引用
try {
    Men& menbm = dynamic_cast<Men&>(q);
    // 轉換成功
     cout << "phuman實際是一個Men類型" << endl;
    // 在這裏操作類Men裏邊的成員函數,成員變量都能夠操作並且安全的操作
    menbm.testfunc();
} catch(std::bad_cast) {
    // 轉換失敗
    out << "phuman不是一個Men類型" << endl;
}

三、typeid運算符

typeid(類型【指針/引用】;也可能typeid表達式

拿到對象類型信息;typeid就會趕回一個常量對象的引用,這個常量對象是一個標準庫類型type_info(類/類類型)

Human* phuman = new Men;
Human& q = *phuman;
cout << typeid(*phuman).name() << endl; // class Men;
cout << typeid(q).name() << endl; // class Men
char a[10] = {5, 1};
int b = 666;
cout << typeid(a).name() << endl; // char[10]
cout << tyepid(b).name() << endl; // int
cout << tyepid(19.6).name() << endl; // double
cout << tyepid("asd").name() << endl; // char const[4]

typeid主要是爲了比較兩個指針是否指向同一種類型的對象

(1)兩個指針定義的類型相同(Human),不管他們new的是啥,typeid都相等。

該例不太符合我們的期盼和要求

Human* phuman = new Men;
Human* phuman2 = new Women;
if (typeid(phuman) == typeid(phuman2)) { // 成立
    cout << "phuman和phuman2是同一種類型[看指針定義]" << endl;
}

比較對象時,看的是new出來的是哪個對象或者該指針指向的是哪個對象,和定義該指針時定義的類型沒關係。

Human* phuman = new Men;
Human* phuman2 = new Men;
Human* phuman3 = phuman2; 
if (typeid(*phuman) == typeid(*phuman2)) { // 成立
    cout << "phuman和phuman2指向的對象類型相同" << endl;
}
if (typeid(*phuman) == typeid(*phuman3)) { // 成立
    cout << "phuman2和phuman3指向的對象類型相同" << endl;
}

基類必須要有虛函數,否則上邊的條件不成立。

切記:只有當基類有虛函數時,編譯器纔會對typeid()中的表達式求值。否則如果某個類型不含有虛函數,則typeid()返回的是表達式的靜態類型(定義時的類型),既然是定義的類型,編譯器就不需要對表達式求值,也能知道表達式的靜態類型。

四、type_info類

typeid機會返回一個常量對象的引用,這個常量對象是一個標準庫;類型type_info(類/類類型)

a)name:名字返回一個c風格字符串

Human* phuman = new Men;
const type_info& tp = typeid(*phuman);
// cout << tp.name() << endl; // class Men

b)==,!=

Human* phuman2 = new Men;
const type_info& tp2 = typeid(*phuman2);
if(tp == tp2) {    // 成立
    cout << "類型相同" << endl;
}
Human* phuman3 = new Women;
const type_info& tp3 = typeid(*phuman3);
if(tp == tp3) {    // 不成立
    cout << "tp和tp3類型相同" << endl;
}

五、RTTI與虛函數表

c++中,如果類裏含有虛函數。編譯器就會對該類產生一個虛函數表

虛函數裏有很多項,每一個項都是一個指針。每個指針指向的是這個類裏的各個虛函數的入口地址。

虛函數表項裏,第一個表象很特殊,它指向的不是虛函數的入口地址,它指向的實際上是咱們這個類所關聯的type_info對象。

Human* phuman = new Men;
const type_info& tp = typeid(*phuman);

phuman對象裏有一個我們看不見的指針,這個指針指向是這個對象所在的類Men裏的虛函數表

參考:https://blog.csdn.net/INGNIGHT/article/details/72372968

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章