C++ 運行時類別識別

運行時動態類型的識別其實應該是多態方面的知識,這裏我直接拿來單獨成章。ios

 

 dynamic_cast和static_cast

 

1.static_cast用法以下:

static_cast < Type-id > ( expression )

該運算符把expression轉換爲type-id類型,但沒有運行時類型檢查來保證轉換的安全性。它主要可用於如下幾種狀況:express


(1)用於類層次結構中基類和派生類之間指針或引用的轉換。進行上行轉換(把派生類的指針或引用轉換成基類表示)是安全的;進行下行轉換(把基類指針或引用轉換成派生類表示)時,因爲沒有動態類型檢查,因此是不安全的。
(2)用於基本數據類型之間的轉換,如把int轉換成char,把int轉換成enum。這種轉換的安全性也要開發人員來保證。
(3)把空指針轉換成目標類型的空指針(不安全!!)。
(4)把任何類型的表達式轉換成void類型。

安全

class Base { };
class Derived:public Base {};
int main()
{
    Base b;
    Derived* pD = &b; //error C2440: 「初始化」: 沒法從「Base *」轉換爲「Derived *」
    pD = static_cast<Derived*> (&b);
    return 0;
}


在上面代碼中pD做爲一派生類指針,將它指向基類是錯誤的(但反過來是能夠的)。函數

能夠用static_cast將基類指針強制轉換爲派生類指針,這樣即可以將pD指向基類對象。spa

但像上面這樣使用static_cast進行向上映射時,是至關危險的,可能會形成沒法跟蹤的運行期錯誤。

指針

C++提供了dynamic_cast操做符,能夠在運行期間檢測類型轉換是否安全。dynamic_cast和static_cast有一樣的語法:
     code

  dynamic_cast < Type-id > ( expression )

 

The dynamic_cast operator in C++ is used for downcasting a reference or pointer to a more specific type in the class hierarchy. Unlike the static_cast, the target of the dynamic_cast must be a pointer or reference to class.


Type-id必須是一個類的的指針或引用,也能夠是 void *,參數expression必須是一個能獲得一個指針或者引用的表達式。

對象

同時,dynamic_cast僅對多態類型有效,也就是說使用dynamic_cast時要求基類中要有虛函數,不然會有編譯錯誤;blog

而static_cast則沒有這個限制。另外dynamic_cast要求轉型的目的類型必須是指針或者引用。這是因爲運行時類型檢查須要運行時類型信息,而這個信息存儲在類的虛函數表中,只有定義了虛函數的類纔有虛函數表,沒有定義虛函數的類是沒有虛函數表的。ci

 

例:

class Base
{
public:
    void fun( ) {};
};
class Derived:public Base { };
int main( )
{
    Base b;
    Derived d;
    Base *pb = &b;
    Derived *pd1 = static_cast<Derived *> (pb);
    Derived *pd2 = dynamic_cast<Derived *>(pb);  // error C2683: 「dynamic_cast」:「Base」不是多態類型
    return 0;
}

若將fun()函數聲明爲虛函數,則不會有上面的錯誤。

若是pb實際指向一個 Derived類型的對象,pd1和pd2是同樣的,而且這兩個指針執行Derived類型的任何操做都是安全的;若是pb實際指向的是一個Base類型的對象,那麼pd1將是一個指向該對象的指針,對它進行Derived類型的操做將是不安全的,而pd2將會獲得一個空指針(即0,由於dynamic_cast失敗)!

 

例:

 

#include <iostream>
using namespace std;
class Base
{
public:
    virtual void fun1()
    {
        cout << "Base::fun1()" << endl;
    }
    virtual ~Base() { }
};
class Derived1: public Base
{
public:
    virtual void fun1()
    {
        cout << "Derived1::fun1()" << endl;
    }
    virtual void fun2()
    {
        cout << "Derived1::fun2()" << endl;
    }
};
class Derived2: public Derived1
{
public:
    virtual void fun1()
    {
        cout << "Derived2::fun1()" << endl;
    }
    virtual void fun2()
    {
        cout << "Derived2::fun2()" << endl;
    }
};
void fun(Base *b)
{
    b->fun1();
    //嘗試將b轉換爲Derived1指針
    Derived1 *d = dynamic_cast<Derived1 *>(b);
    //判斷轉換是否成功
    if (d != 0)
        d->fun2();
}
int main()
{
    Base b;
    fun(&b);
    Derived1 d1;
    fun(&d1);
    Derived2 d2;
    fun(&d2);
    return 0;
}

 

運行結果:

Base::fun1()
Derived1::fun1()
Derived1::fun2()
Derived2::fun1()
Derived2::fun2()

 

 

typeid 獲取運行時類型信息

 

typeid語法形式:
typeid ( 表達式 )
typeid ( 類型說明符 )


typeid功能
得到表達式或類型說明符的類型信息,表達式有多態類型時,會被求值,並獲得動態類型信息;不然,表達式不被求值,只能獲得靜態的類型信息。

類型信息用type_info對象表示,type_info是typeinfo頭文件中聲明的類;

typeid的結果是type_info類型的常引用;
能夠用type_info的重載的「==」、「!=」操做符比較兩類型的異同;
type_info的name成員函數返回類型名稱,類型爲const char *。

#include <iostream>
#include <typeinfo>
using namespace std;
class Base
{
public:
    virtual ~Base() { }
};
class Derived: public Base { };
void fun(Base *b)
{
    const type_info &info1 = typeid(b);
    const type_info &info2 = typeid(*b);
    cout<<"typeid(b): "<<info1.name ()<<endl;
    cout<<"typeid(*b): "<<info2.name ()<<endl;
    if(info2 == typeid(Base))
        cout<<"A base class!"<<endl;
    else
        cout<<"A Derived class!"<<endl;
    cout<<endl;
}
int main()
{
    Base b;
    fun(&b);
    Derived d;
    fun(&d);
    return 0;
}

運行結果:

typeid(b): P4Basetypeid(*b): 4BaseA base class!typeid(b): P4Basetypeid(*b): 7DerivedA Derived class!

相關文章
相關標籤/搜索