原文:https://www.cnblogs.com/renzhuang/articles/10466420.html
參考:https://www.cnblogs.com/QG-whz/p/4517336.html
- dynamic_cast主要用於“安全地向下轉型”
dynamic_cast用於類繼承層次間的指針或引用轉換。主要還是用於執行“安全的向下轉型(safe downcasting)”,也即是基類對象的指針或引用轉換為同一繼承層次的其他指針或引用。至於“先上轉型”(即派生類指針或引用類型轉換為其基類類型),本身就是安全的,盡管可以使用dynamic_cast進行轉換,但這是沒必要的, 普通的轉換已經可以達到目的,畢竟使用dynamic_cast是需要開銷的。
#include <iostream> using namespace std; class Base { public: Base() {}; virtual void Show() { cout << "This is Base calss"; } }; class Derived :public Base { public: Derived() {}; void Show() { cout << "This is Derived class"; } void Dfun() { cout << "Dfun"; } }; int main11() { Base *base; Derived *der = new Derived; //base = dynamic_cast<Base*>(der); //正確,但不必要。 base = der; //先上轉換總是安全的 base->Show(); // This is Derived class 子類轉父類,安全,且轉換后,還是調用子類的虛函數, //base->Dfun(); //ERR: Dfun不是Base的成員 子類轉父類,安全,但是轉換后,不能再使用子類的非虛函數, return 0; }
- dynamic_cast與繼承層次的指針
對於“向下轉型”有兩種情況。一種是基類指針所指對象是派生類類型的,這種轉換是安全的;另一種是基類指針所指對象為基類類型,在這種情況下dynamic_cast在運行時做檢查,轉換失敗,返回結果為0;
#include <iostream> using namespace std; class Base { public: Base() {}; virtual void Show() { cout << "This is Base calss"<<endl; } }; class Derived :public Base { public: Derived() {}; virtual void Show() { cout << "This is Derived class"<<endl; } void Dfun() { cout << "Dfun"<<endl; } char name[10]; }; int main222() { //場景一: Base *base = new Base; //Derived *der = base; //error C2440 : “初始化”: 無法從“Base * ”轉換為“Derived *” Derived *der = dynamic_cast<Derived*>(base); //執行完后 der是空指針,所以這個轉換時無意義的 //der->Show(); //編譯沒問題,運行就會報 der是nulptr的段錯誤。 der->Dfun(); //OK 實際上同下一條語句 //((Derived*)(0))->Dfun(); //OK //cout<<sizeof(((Derived*)(0))->name)<<endl; // OK 10 //正確使用場景: 父類指針指向子類對象,需要使用子類對象的非虛接口時 Base *pB = new Derived; //Derived *pD = pB; //“初始化”: 無法從“Base * ”轉換為“Derived *” 解析:多態是運行時才有的, Derived *pD = dynamic_cast<Derived*>(pB); pD->Dfun(); // 如果直接使用 pB->Dfun(); 就會報錯,error C2039: 'Dfun' : is not a member of 'Base' cout << sizeof(pD->name)<<endl; // OK 10 pD->Show(); return 0; }
- dynamic_cast和引用類型
在前面的例子中,使用了dynamic_cast將基類指針轉換為派生類指針,也可以使用dynamic_cast將基類引用轉換為派生類引用。
同樣的,引用的向上轉換總是安全的:
Derived c; Derived & der2= c; Base & base2= dynamic_cast<Base&>(der2);//向上轉換,安全 base2.Show();
所以,在引用上,dynamic_cast依舊是常用於“安全的向下轉型”。與指針一樣,引用的向下轉型也可以分為兩種情況,與指針不同的是,並不存在空引用,所以引用的dynamic_cast檢測失敗時會拋出一個bad_cast異常:
int main() { //第一種情況,轉換成功 Derived b ; Base &base1= b; Derived &der1 = dynamic_cast<Derived&>(base1); cout<<"第一種情況:"; der1.Show(); cout<<endl; //第二種情況 Base a ; Base &base = a ; cout<<"第二種情況:"; try{ Derived & der = dynamic_cast<Derived&>(base); } catch(bad_cast) { cout<<"轉化失敗,拋出bad_cast異常"<<endl; } system("pause"); }
運行結果:
- 使用dynamic_cast轉換的Base類至少帶有一個虛函數
當一個類中擁有至少一個虛函數的時候,編譯器會為該類構建出一個虛函數表(virtual method table),虛函數表記錄了虛函數的地址。如果該類派生了其他子類,且子類定義並實現了基類的虛函數,那么虛函數表會將該函數指向新的地址。虛表是C++多態實現的一個重要手段,也是dynamic_cast操作符轉換能夠進行的前提條件。當類沒有虛函數表的時候(也即一個虛函數都沒有定義),dynamic_cast無法使用RTTI,不能通過編譯,報錯:error C2683: “dynamic_cast”:“Base”不是多態類型。
當然,虛函數表的建立對效率是有一定影響的,構建虛函數表、由表查詢函數 都需要時間和空間上的消耗。所以,除了必須聲明virtual(對於一個多態基類而言),不要輕易使用virtual函數。對於虛函數的進一步了解,可以查看《Effective C++》