漫談繼承技術(五)

       如果沒有繼承,類只是具有一些相關行爲的數據結構,這只是對過程語言的一大改進,而繼承則開闢了完全不同的新天地。通過繼承,可以在已有類的基礎上創建新類。這樣,類就成爲可重用和可擴展的組件。博主的《漫談繼承技術》系列博文將講述各種利用繼承功能的方法。學習繼承的語法,和利用繼承的一些複製技術。本篇博文先給大家介紹一下dynamic_cast在繼承中的使用,希望對大家加深對繼承技術的理解有一定的幫助。

dynamic_cast

語法:dynamic_cast < type-id> ( expression )

該運算符把expression轉換成type-id類型的對象。type-id必須是類的指針、類的引用或者void *

       dynamic_cast的轉換是在運行時進行的,它的一個好處是會在運行時做類型檢查。如果對象的類型不是期望的類型,它會在指針轉換的時候返回nullptr,而在引用轉換的時候會拋出std::bad_cast標準異常。

       dynamic_cast一般只在基類指向派生類對象的指針之間或引用之間進行類型轉換。dynamic_cast運行時類型檢查需要運行時類型信息,而這個信息存儲在類的虛函數表中。如果沒有虛函數表則會報錯。只要基類有一個虛函數,編譯器就會產生一個虛表指針指向虛函數表,虛函數表中存儲的是虛函數的入口地址。dynamic_cast在運行時遍歷繼承樹,只要是在繼承樹上的類都可以相互轉換。咱們舉個簡單的栗子吧。

 

#include <iostream>

using namespacestd;

 

struct A

{

    virtualvoidf() { }

};

struct B: public A{ };

struct C{ };

 

void fun()

{

    A a;

    B b;

    A* ap = &b;

 

    //指針轉換

    B* b1 = dynamic_cast<B*>(&a);  //nullptr, because 'a' is not a 'B'

    if(nullptr== b1)

    {

        cout<< "b1 is nullptr"<<endl;

    }

 

    B* b2 = dynamic_cast<B*>(ap);  //'b'

    if(nullptr== b2)

    {

        cout<< "b2 is nullptr"<< endl;

    }

 

    C* c = dynamic_cast<C*>(ap);   //nullptr

    if(nullptr== c)

    {

        cout<< "c is nullptr"<< endl;

    }

 

    //可以將void*作爲中間類型,用來存儲實際的類類型指針

    //待需要的時候再提供reinterpret_cast轉換回去

    void*pv = dynamic_cast<void*>(ap);

    //dynamic_cast的操作數必須是指向完整類類型的指針或引用

    //B*pb = dynamic_cast<B*>(pv);

    A *pa = reinterpret_cast<A*>(pv);

    consttype_info& timeinfo = typeid(*pa);

    cout<< "timeinfo: "<< timeinfo.name() << endl;

 

    //引用轉換

    A& ar = dynamic_cast<A&>(*ap); // Ok.

    B& br = dynamic_cast<B&>(*ap); // Ok.

    //C&cr = dynamic_cast<C&>(*ap); // std::bad_cast

}

 

int main(intargc,char**argv)

{

    fun();

 

    return0;

}

 

程序運行結果:

 

        如果你使用的是VC 6.0,可能回報以下警告信息,那是因爲你還沒用打開動態類型信息支持。使用'dynamic_cast'需要打開run-time   type   information  支持,否則會報以上警告。


        打開方式:菜單project -> setting ->c /c++ -> c++ language -> enable   run-time  type   information   (RTTI)(勾選此項)

        以上是VC 6.0 打開動態類型信息支持的方式,VS是自動支持的,就不用再打開。

 

注意:僅在必要的時候才使用向下轉型,一定要使用dynamic_cast。舉個栗子。

#include <iostream>

using namespacestd;

 

struct A

{

    virtualvoidf() { }

};

struct B: public A{ };

struct C: public A{ };

 

void fun()

{

    //基類引用

    A &refa = A();

    A &refb = B();

    A &refc = C();

 

    //初始化派生類指針

    C *pc = nullptr;

 

    //向下轉型

    //基類A對象指針向派生類C指針轉型,此轉型是不安全的,返回nullptr

    pc = dynamic_cast<C*>(&refa);

    if(nullptr== pc)

    {

        cout<< "pc = dynamic_cast<C*>(&refa), pc isnullptr" << endl;

    }

 

    //派生類B對象指針向派生類C指針轉型,此轉型是不安全的,返回nullptr

    pc = dynamic_cast<C*>(&refb);

    if(nullptr== pc)

    {

        cout<< "pc = dynamic_cast<C*>(&refb), pc isnullptr" << endl;

    }

 

    //派生類C對象指針向派生類C指針轉型,此轉型是安全的

    pc = dynamic_cast<C*>(&refc);

    if(nullptr== pc)

    {

        cout<< "pc = dynamic_cast<C*>(&refc), pc isnullptr" << endl;

    }

 

    //平行轉型

    B *pB = new B();

    C *pC = new C();

    pc = nullptr;

 

    //派生類B對象指針向派生類C指針轉型,此轉型是不安全的,返回nullptr

    pc = dynamic_cast<C*>(pB);

    if(nullptr== pc)

    {

        cout<< "pc = dynamic_cast<C*>(pB), pc isnullptr" << endl;

    }

 

    //釋放資源

    deletepB;

    pB = nullptr;

 

    deletepC;

    pC = nullptr;

 

}

 

int main()

{

    fun();

 

    return0;

}

 

程序運行結果:


注意到了麼?dynamic_cast拒絕沒有意義的轉型

       向上轉型是安全的,如果是對象的話,會發生對象切片的情況,但這是安全的,只是不能再通過轉換後的基類對象調用派生類成員方法和使用派生類數據成員。那怎麼才能避免對象切片呢?使用基類指針或者引用就可以避免。這部分代碼我就不敲了,大家自己敲代碼試試吧。


         如果想了解更多關於繼承技術相關的知識,請關注博主《漫談繼承技術》系列博文,相信你能夠在那裏尋找到更多有助你快速成長和深入你對繼承相關的知識和一些複製的技術理解和掌握。
發佈了81 篇原創文章 · 獲贊 18 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章