二級(jí)考試C++基礎(chǔ):虛函數(shù)與多態(tài)性

字號(hào):

因?yàn)轸~(yú)的呼吸是吐泡泡,和一般動(dòng)物的呼吸不太一樣,所以我們?cè)趂ish類中重新定義breathe方法。我們希望如果對(duì)象是魚(yú),就調(diào)用fish類的breathe()方法,如果對(duì)象是動(dòng)物,那么就調(diào)用animal類的breathe()方法。程序代碼如例2-16所示(EX08.CPP)。
    例2-16
    #include
    class animal
    {
    public:
    void eat()
    {
    cout<<"animal eat"<    }
    void sleep()
    {
    cout<<"animal sleep"<    }
    void breathe()
    {
    cout<<"animal breathe"<    }
    };
    class fish:public animal
    {
    public:
    void breathe()
    {
    cout<<"fish bubble"<    }
    };
    void fn(animal *pAn)
    {
    pAn->breathe();
    }
    void main()
    {
    animal *pAn;
    fish fh;
    pAn=&fh;
    fn(pAn);
    }
    我們?cè)趂ish類中重新定義了breathe()方法,采用吐泡泡的方式進(jìn)行呼吸。接著定義了一個(gè)全局函數(shù)fn(),指向animal類的指針作為fn()函數(shù)的參數(shù)。在main()函數(shù)中,定義了一個(gè)fish類的對(duì)象,將它的地址賦給了animal類的指針變量pAn,然后調(diào)用fn()函數(shù)??吹竭@里,我們可能會(huì)有些疑惑,照理說(shuō),C++是強(qiáng)類型的語(yǔ)言,對(duì)類型的檢查應(yīng)該是非常嚴(yán)格的,但是,我們將fish類的對(duì)象fh的地址直接賦給指向animal類的指針變量,C++編譯器居然不報(bào)錯(cuò)。這是因?yàn)閒ish對(duì)象也是一個(gè)animal對(duì)象,將fish類型轉(zhuǎn)換為animal類型不用強(qiáng)制類型轉(zhuǎn)換,C++編譯器會(huì)自動(dòng)進(jìn)行這種轉(zhuǎn)換。反過(guò)來(lái),則不能把a(bǔ)nimal對(duì)象看成是fish對(duì)象,如果一個(gè)animal對(duì)象確實(shí)是fish對(duì)象,那么在程序中需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換,這樣編譯才不會(huì)報(bào)錯(cuò)。
    讀者可以猜想一下例2-16運(yùn)行的結(jié)果,輸出的結(jié)果應(yīng)該是“animal breathe”,還是“fish bubble”呢?
    為什么輸出的結(jié)果不是“fish bubble”呢?這是因?yàn)樵谖覀儗ish類的對(duì)象fh的地址賦給pAn時(shí),C++編譯器進(jìn)行了類型轉(zhuǎn)換,此時(shí)C++編譯器認(rèn)為變量pAn保存就是animal對(duì)象的地址。當(dāng)在fn函數(shù)中執(zhí)行pAn->breathe()時(shí),調(diào)用的當(dāng)然就是animal對(duì)象的breathe函數(shù)。
    為了幫助讀者更好地理解對(duì)象類型的轉(zhuǎn)換,我們給出了fish對(duì)象內(nèi)存模型。
    當(dāng)我們構(gòu)造fish類的對(duì)象時(shí),首先要調(diào)用animal類的構(gòu)造函數(shù)去構(gòu)造animal類的對(duì)象,然后才調(diào)用fish類的構(gòu)造函數(shù)完成自身部分的構(gòu)造,從而拼接出一個(gè)完整的fish對(duì)象。當(dāng)我們將fish類的對(duì)象轉(zhuǎn)換為animal類型時(shí),該對(duì)象就被認(rèn)為是原對(duì)象整個(gè)內(nèi)存模型的上半部分,也就是圖2.13中的“animal的對(duì)象所占內(nèi)存”。當(dāng)我們利用類型轉(zhuǎn)換后的對(duì)象指針去調(diào)用它的方法時(shí),自然也就是調(diào)用它所在的內(nèi)存中的方法。因此,出現(xiàn)如圖2.12所示的結(jié)果,也就順理成章了。
    現(xiàn)在我們?cè)赼nimal類的breathe()方法前面加上一個(gè)virtual關(guān)鍵字,結(jié)果如例2-17所示。
    例2-17
    #include
    class animal
    {
    public:
    void eat()
    {
    cout<<"animal eat"<    }
    void sleep()
    {
    cout<<"animal sleep"<    }
    virtual void breathe()
    {
    cout<<"animal breathe"<    }
    };
    class fish:public animal
    {
    public:
    void breathe()
    {
    cout<<"fish bubble"<    }
    };
    void fn(animal *pAn)
    {
    pAn->breathe();
    }
    void main()
    {
    animal *pAn;
    fish fh;
    pAn=&fh;
    fn(pAn);
    }
    用virtual關(guān)鍵字申明的函數(shù)叫做虛函數(shù)。運(yùn)行例2-17這個(gè)程序,結(jié)果調(diào)用的是fish類的呼吸方法:
    這就是C++中的多態(tài)性。當(dāng)C++編譯器在編譯的時(shí)候,發(fā)現(xiàn)animal類的breathe()函數(shù)是虛函數(shù),這個(gè)時(shí)候C++就會(huì)采用遲綁定(late binding)技術(shù)。也就是編譯時(shí)并不確定具體調(diào)用的函數(shù),而是在運(yùn)行時(shí),依據(jù)對(duì)象的類型(在程序中,我們傳遞的fish類對(duì)象的地址)來(lái)確認(rèn)調(diào)用的是哪一個(gè)函數(shù),這種能力就叫做C++的多態(tài)性。我們沒(méi)有在breathe()函數(shù)前加virtual關(guān)鍵字時(shí),C++編譯器在編譯時(shí)就確定了哪個(gè)函數(shù)被調(diào)用,這叫做早期綁定(early binding)。
    C++的多態(tài)性是通過(guò)遲綁定技術(shù)來(lái)實(shí)現(xiàn)的,關(guān)于遲綁定技術(shù),讀者可以參看相關(guān)的書(shū)籍,在這里,我們就不深入講解了。
    C++的多態(tài)性用一句話概括就是:在基類的函數(shù)前加上virtual關(guān)鍵字,在派生類中重寫(xiě)該函數(shù),運(yùn)行時(shí)將會(huì)根據(jù)對(duì)象的實(shí)際類型來(lái)調(diào)用相應(yīng)的函數(shù)。如果對(duì)象類型是派生類,就調(diào)用派生類的函數(shù);如果對(duì)象類型是基類,就調(diào)用基類的函數(shù)。