溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C++的RTTI和cast運(yùn)算符如何使用

發(fā)布時(shí)間:2022-08-25 11:23:19 來源:億速云 閱讀:122 作者:iii 欄目:開發(fā)技術(shù)

本文小編為大家詳細(xì)介紹“C++的RTTI和cast運(yùn)算符如何使用”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“C++的RTTI和cast運(yùn)算符如何使用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學(xué)習(xí)新知識(shí)吧。

    1. RTTI

    RTTI是運(yùn)行階段類型識(shí)別(Running Type Identificarion)的簡(jiǎn)稱。

    如何知道指針指向的是哪種對(duì)象?

    這是個(gè)很常見的問題,由于我們?cè)试S使用基類指針指向派生類,所以基類指針指向的對(duì)象可能是基類對(duì)象,也可能是派生類對(duì)象。但是我們需要知道對(duì)象種類,因?yàn)槲覀冃枰褂谜_的類方法。

    RTTI能解決上述問題。

    1.1 dynamic_cast運(yùn)算符

    dynamic_cast是最常用的RTTI組件,它不能回答"指針指向的是哪類對(duì)象",但是它能回答"是否可以安全的將對(duì)象的地址賦給特定類型指針"。

    什么是安全?

    例如:A是基類,B是A的派生類,C是B的派生類。那么將BC對(duì)象的地址賦給A指針是安全的,而反向就是不安全的。因?yàn)楣欣^承滿足is-a關(guān)系,指針和引用進(jìn)行向上轉(zhuǎn)換是安全的,向下轉(zhuǎn)換就是不安全的。

    dynamic_cast的用法是:

    dynamic_cast<Type*>(ptr)dynamic_cast<Type&>(ref)

    可以看到的是,dynamic_cast是一種類型轉(zhuǎn)換符,它支持指針間的轉(zhuǎn)換,他將ptr的類型轉(zhuǎn)換成Type*,如果這種轉(zhuǎn)換是安全的,那會(huì)返回轉(zhuǎn)換后的指針;如果不安全那就會(huì)返回空指針。對(duì)于引用的話,他將ref的類型轉(zhuǎn)換成Type&,如果這種轉(zhuǎn)換是安全的,那就返回轉(zhuǎn)換后的引用;如果不安全那就會(huì)引發(fā)bad_cast異常。

    總之,dynamic_cast類型轉(zhuǎn)換運(yùn)算符比C風(fēng)格的類型轉(zhuǎn)換要安全的多,而且它能夠判斷轉(zhuǎn)換是否安全。

    //RTTI1.cpp
    #include<iostream>
    #include<cstdlib>
    #include<ctime>
    using namespace std;
    class Grand
    {
    protected:
        int hold;
    public:
        Grand(int h=0):hold(h){};
        virtual void Speak() const {cout<<"I am Grand class!\n";}
    };
    class Superb:public Grand
    {
    public:
        Superb(int h=0):Grand(h){}
        virtual void Speak() const {cout<<"I am a superb class!\n";}
        virtual void Say() const {cout<<"the value: "<<Grand::hold<<endl;}
    };
    class Magnificent:public Superb
    {
    private:
        char ch;
    public:
        Magnificent(int h=0,char c='A'):Superb(h),ch(c){}
        virtual void Speak() const{cout<<"I am a Magnificent class!\n";}
        virtual void Say() const{cout<<"the character: "<<ch<<endl<<"the value: "<<Grand::hold<<endl;}
    };
    Grand *GetOne();
    int main()
    {
        srand(time(0));
        Grand *pg;
        Superb *ps;
        for(int i=0;i<5;i++)
        {
            pg=GetOne();
            pg->Speak();
            if(ps=dynamic_cast<Superb*>(pg))
            {
                ps->Say();
            }
        }
        delete pg;
        return 0;
    }
    Grand * GetOne()
    {
        Grand *p;
        switch (rand()%3)
        {
            case 0:
                p=new Grand(rand()%100);
                break;
            case 1:
                p=new Superb(rand()%100);
                break;
            case 2:
                p=new Magnificent(rand()%100,'A'+rand()%26);
                break;
        }
        return p;
    }

    I am a Magnificent class!
    the character: I
    the value: 74
    I am a superb class!
    the value: 50
    I am Grand class!
    I am Grand class!
    I am a Magnificent class!
    the character: V
    the value: 99

    上面這個(gè)例子說明了重要的一點(diǎn):盡可能使用虛函數(shù),而只在必要時(shí)使用RTTI。

    如果將dynamic_cast用于引用,當(dāng)請(qǐng)求不安全的時(shí)候,會(huì)引發(fā)bad_cast異常,這種異常時(shí)從exception類派生而來的,它在頭文件typeinfo中定義。

    則我們可以使用如下方式處理異常:

    #include<typeinfo>
    ...
    try{
        Superb & rs= dynamic_cast<Superb &>(rg);
        ....
    }
    catch(bad_cast &)
    {
        ...
    };

    1.2 typeid運(yùn)算符

    type_info類是在頭文件typeinfo中定義的一個(gè)類。type_info類重載了==!=運(yùn)算符。

    typeid是一個(gè)運(yùn)算符,它返回一個(gè)對(duì)type_info的引用,它可以接受2種參數(shù):類名和對(duì)象。typeid是真正回答了"指針指向的是哪類對(duì)象"。

    (實(shí)際上,typeid也可以用于其他類型,例如結(jié)構(gòu)體,函數(shù)指針,各種類型,只要sizeof能接受的,typeid都能接受)

    用法:

    typeid(Magnificent)==typeid(*pg)

    如果pg是一個(gè)空指針,則會(huì)引發(fā)bad_typeid異常。

    type_info類中包含一個(gè)name()接口,該接口返回字符串,一般情況下是類的名稱。

    cout<<typeid(*pg).name()<<".\n"

    例子:

    //RTTI2.cpp
    #include<iostream>
    #include<cstdlib>
    #include<ctime>
    #include<typeinfo>
    using namespace std;
    class Grand
    {
    protected:
        int hold;
    public:
        Grand(int h=0):hold(h){};
        virtual void Speak() const {cout<<"I am Grand class!\n";}
    };
    class Superb:public Grand
    {
    public:
        Superb(int h=0):Grand(h){}
        virtual void Speak() const {cout<<"I am a superb class!\n";}
        virtual void Say() const {cout<<"the value: "<<Grand::hold<<endl;}
    };
    class Magnificent:public Superb
    {
    private:
        char ch;
    public:
        Magnificent(int h=0,char c='A'):Superb(h),ch(c){}
        virtual void Speak() const{cout<<"I am a Magnificent class!\n";}
        virtual void Say() const{cout<<"the character: "<<ch<<endl<<"the value: "<<Grand::hold<<endl;}
    };
    Grand *GetOne();
    int main()
    {
        srand(time(0));
        Grand *pg;
        Superb *ps;
        for(int i=0;i<5;i++)
        {
            pg=GetOne();
            cout<<"Now processing type "<<typeid(*pg).name()<<".\n";
            pg->Speak();
            if(ps=dynamic_cast<Superb*>(pg))
            {
                ps->Say();
            }
            if(typeid(Magnificent)==typeid(*pg))
            {
                cout<<"Yes,you are really magnificent.\n";
            }
        }
        delete pg;
        return 0;
    }
    Grand * GetOne()
    {
        Grand *p;
        switch (rand()%3)
        {
            case 0:
                p=new Grand(rand()%100);
                break;
            case 1:
                p=new Superb(rand()%100);
                break;
            case 2:
                p=new Magnificent(rand()%100,'A'+rand()%26);
                break;
        }
        return p;
    }

    Now processing type 6Superb.      
    I am a superb class!
    the value: 64
    Now processing type 11Magnificent.
    I am a Magnificent class!
    the character: Y
    the value: 30
    Yes,you are really magnificent.   
    Now processing type 5Grand.       
    I am Grand class!
    Now processing type 11Magnificent.
    I am a Magnificent class!
    the character: C
    the value: 3
    Yes,you are really magnificent.   
    Now processing type 5Grand.       
    I am Grand class!

    注意,你可能發(fā)現(xiàn)了typeid遠(yuǎn)比dynamic_cast優(yōu)秀的多,typeid會(huì)直接告訴你類型是什么,而dynamic_cast只告訴你是否可以類型轉(zhuǎn)換。但是typeid的效率比dynamic_cast低很多,所以,請(qǐng)優(yōu)先考慮dynamic_cast和虛函數(shù),如果不行才使用typeid。

    2. cast運(yùn)算符

    C語言中的類型轉(zhuǎn)換符太過隨意,C++創(chuàng)始人認(rèn)為,我們應(yīng)該使用嚴(yán)格的類型轉(zhuǎn)換,則C++提供了4個(gè)類型轉(zhuǎn)換運(yùn)算符:

    • dynamic_cast

    • const_cast

    • static_cast

    • reinterpret_cast

    dynamic_cast運(yùn)算符已經(jīng)介紹過了,它的用途是,使得類層次結(jié)構(gòu)中進(jìn)行向上轉(zhuǎn)換,而不允許其他轉(zhuǎn)換。

    const_cast運(yùn)算符,用于除去或增加 類型的constvolatile屬性。

    它的語法是:

    const_cast<type-name>(expression)

    如果類型的其他方面也被修改,則上述類型轉(zhuǎn)換就會(huì)出錯(cuò),也就是說,除了cv限定符可以不同外,type_nameexpression的類型必須相同。

    提供該運(yùn)算符的目的是:有時(shí)候我們需要一個(gè)值:它在大多數(shù)情況下是常量,而有時(shí)候我們需要更改它。

    看一個(gè)有趣的例子:

    //cast運(yùn)算符1.cpp
    //cast運(yùn)算符1.cpp
    #include <iostream>
    int main()
    {
        using std::cout;
        using std::endl;
        volatile const int a=100;
        volatile const int & ra=a;//無法通過ra修改a
        int &change_a=const_cast<int &>(ra);//可以通過change_a修改a;
        change_a=255;
        cout<<a<<endl;
    }

    上面最后這個(gè)a常量被修改成255,這是我們預(yù)期的結(jié)果。但是如果我把volatile關(guān)鍵詞去掉,那么a的值還是100。其實(shí)是編譯器在這里玩了個(gè)小聰明,const int a=100,編譯器認(rèn)為a就不會(huì)發(fā)生改變了,所以就把a(bǔ)放在了寄存器中,因?yàn)樵L問寄存器要比訪問內(nèi)存單元快的多,每次都從寄存器中取數(shù)據(jù),但是后來a在內(nèi)存中發(fā)生變化后,寄存器中的數(shù)據(jù)沒有發(fā)生變化,所以打印的是寄存器中的數(shù)據(jù)。解決這一問題,就使用volatile關(guān)鍵詞,那么對(duì)a的存取都是直接對(duì)內(nèi)存做操作的。

    static_cast運(yùn)算符

    它的語法是:

    static_cast<type-name>(expression)

    僅當(dāng)type-name可被隱式轉(zhuǎn)換成expression所屬的類型或者expression可以隱式轉(zhuǎn)換成type-name類型時(shí),上述轉(zhuǎn)換才合法,否則出現(xiàn)bad_cast異常。

    例如,枚舉值可以隱式轉(zhuǎn)換成整型,那么整型就可以通過static_cast轉(zhuǎn)換成枚舉型。

    總之,static_cast只允許"相似"的類型間的轉(zhuǎn)換,而不允許"差異很大"的類間的轉(zhuǎn)換。

    reinterpret_cast運(yùn)算符:重新詮釋類型,進(jìn)行瘋狂的類型轉(zhuǎn)換

    它的語法時(shí):

    reinterpret_cast<type-name>(expression)

    它可以進(jìn)行類型間"不可思議"的互換,但是它不允許刪除const,也不允許進(jìn)行喪失數(shù)據(jù)的轉(zhuǎn)換

    例如下面這兩個(gè)例子:

    #include<iostream>
    int main()
    {
        using namespace std;
        struct dat {short a; short b;};
        long value =0xA224B118;
        dat *pd=reinterpret_cast<dat*>(&value);
        cout<<hex<<pd->a;
    }
    #include<iostream>
    void fun()
    {
        std::cout<<"hello world!\n";
    }
    int main()
    {
        using namespace std;
        void (*foo)();
        foo=fun;
        int* p=reinterpret_cast<int*>(foo);
    }

    通常,reinterpret_cast轉(zhuǎn)換符適用于底層編程技術(shù),是不可移植的,因?yàn)椴煌到y(tǒng)可能使用不同大小,不同順序來存儲(chǔ)同一類型的數(shù)據(jù)。

    總之,C語言類型轉(zhuǎn)換比reinterpret_cast還要"瘋狂",非常不安全,所以推薦使用cast運(yùn)算符來實(shí)現(xiàn)類型轉(zhuǎn)換。

    讀到這里,這篇“C++的RTTI和cast運(yùn)算符如何使用”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI