天天看點

dynamic_cast的使用條件

今天從學長那裡聽來一道騰訊的面試題,問的是C++中的dynamic_cast在什麼情況下是錯誤的?我認為這個問題的更好的描述是dynamic_cast的使用條件是什麼?

C++提供了兩種方式來支援RTTI,dynamic_cast是其中一種,另一種是typeid()。表面上看,dynamic_cast有兩種形式:

Base *pBase = new Derived();

Derived *pDerived = dynamic_cast<Derived *> pBase;

Base refBase = Derived();

Derived &refDerived = dynamic_cast<Derived &> refBase

即dynamic_cast用于将基類的引用或指針轉化為派生類的引用或指針。但使用條件是:1、基類的指針或引用确實綁定到派生類的對象上;2、隻有當基類至少含有一個虛函數的時候才能使用dynamic_cast。原因是RTTI機制依賴于虛函數表(inside C++ object model第一章給出了解釋),而dynamic_cast是RTTI的一種,是以必須要有虛函數表的支援,也就是要虛類中至少有一個虛函數。

那麼,dynamic_cast有什麼好處呢?

我們知道,基類的指針即使指向的是派生類的對象,但通過基類的指針也隻能通路到基類中包含的public成員,那如果想要通路派生類中新增的public成員呢?這就需要将基類的的指針動态類型轉化為派生類的指針。見以下代碼:

#include<iostream>
using namespace std;
class base{
public:
    base(int x):a(x){ }
    virtual void fcn(){ }  //要有虛函數
    int a;
};

class derived:public base{
public:
    derived(int x,int y):base(x),b(y){ }
    int b;
};

int main(){
    base *pbase = new derived(100, 200);
    cout << pbase->a << endl;
    //cout << pbase->b << endl;  //不能通過基類的指針通路派生類新增的public成員
    
    derived *pderived = dynamic_cast<derived *>(pbase);
    
    cout << pderived->a << endl;
    cout << pderived->b << endl;
}
           

如何對dynamic_cast轉化是否成功進行判斷,進而選擇進行基類的操作還是進行派生類的操作呢?見下面的代碼:

#include<iostream>
using namespace std;
class base{
public:
    base(int x):a(x){ }
    virtual void fcn(){ }  //要有虛函數
    int a;
};

class derived:public base{
public:
    derived(int x,int y):base(x),b(y){ }
    int b;
};

int main(){
    base *pbase0 = new base(100);
    base *pbase1 = new derived(100, 200);


    if(NULL != dynamic_cast<derived *>(pbase0)){
        cout << dynamic_cast<derived *>(pbase0)->b << endl;
        cout<<"derived"<<endl;
    }else{
        cout << pbase0->a << endl;
        cout<<"base"<<endl;
    }

    if(derived *pderived = dynamic_cast<derived *>(pbase1)){
        cout << dynamic_cast<derived *>(pbase1)->b << endl;
        cout<<"derived"<<endl;
    }else{
        cout << pbase1->a << endl;
        cout<<"base"<<endl;
    }
}
           

運作結果如下:

100

base

200

derived

《C++ primer》第5版P731上給出了以下代碼架構:

if(Derived *dp = dynamic_cast<derived *>(bp)){

//使用bp指向的Derived對象

}else{

//使用bp指向的Base對象

}

在vs和gcc下測試過:如果bp指向的不是派生類的對象而是基類的對象的話,那麼兩種編譯器下都不能通過編譯!!!這樣的運作時動态類型判斷也就沒有意義了。這種寫法值得商榷!

如果是引用的話,當轉化不成功時會抛出std::bad_cast異常,詳見《C++ primer》第5版P731。

繼續閱讀