多態(tài),虛函數(shù),純虛函數(shù),虛函數(shù)表

多態(tài)性:一個(gè)接口,多種方法.程序在運(yùn)行時(shí)才確定調(diào)用的函數(shù),是 oop 的核心概念.

  • 多態(tài)性通過虛函數(shù)來(lái)實(shí)現(xiàn),子類可以重新定義父類(重寫:override).

重寫有兩種,一種是重寫虛函數(shù)(體現(xiàn)多態(tài)),另一種就是重寫成員函數(shù)(并沒有體現(xiàn))

和重寫相對(duì)的另一個(gè)概念是重載(overloading),指的是多個(gè)重名的函數(shù)他們的參數(shù)列表不同(個(gè)數(shù),類型),編譯器通過函數(shù)的調(diào)用參數(shù)列表來(lái)決定調(diào)用的

  • 多態(tài)和非多態(tài)的本質(zhì)區(qū)別在于:早綁定和晚綁定.函數(shù)調(diào)用的地址是編譯期間就能確定的,那就是非多態(tài);需要在運(yùn)行的時(shí)候才確定,那就是多態(tài)
  • 面向?qū)ο笕筇匦?br> 1. 封裝:模塊化代碼,實(shí)現(xiàn)代碼重用
    2. 繼承擴(kuò)展已經(jīng)實(shí)現(xiàn)的代碼,實(shí)現(xiàn)代碼重用
    3. 多態(tài): 實(shí)現(xiàn)接口重用,同一個(gè)接口可以自適應(yīng)到各自對(duì)象的實(shí)現(xiàn)方法上面去.
  • 實(shí)現(xiàn)多態(tài)的方法
    1. 聲明基類指針,指向一個(gè)子類對(duì)象
    2. 調(diào)用虛函數(shù)
    3. 如果沒有多態(tài)性,則調(diào)用的函數(shù)將一直是基類的相應(yīng)函數(shù),即,函數(shù)調(diào)用的地址是固定的,不能實(shí)現(xiàn)一個(gè)接口,多種方法.
#include<iostream>  
using namespace std;  
  
class A  
{  
public:  
    void foo()  
    {  
        printf("1\n");  
    }  
    virtual void fun()  
    {  
        printf("2\n");  
    }  
};  
class B : public A  
{  
public:  
    void foo()  
    {  
        printf("3\n");  
    }  
    void fun()  
    {  
        printf("4\n");  
    }  
};  
int main(void)  
{  
    A a;  
    B b;  
    A *p = &a;  
    p->foo();  
    p->fun();  
    p = &b;  
    p->foo();  
    p->fun();  
    return 0;  
}  

輸出為

1 2 1 4

對(duì)于輸出1 2是沒有問題的,在第三和第四個(gè)輸出的時(shí)候,因?yàn)榛愔羔樦赶蛄俗宇?而foo()函數(shù)沒有被虛擬化,所以,這是一個(gè)早綁定,只能調(diào)用基類的同名函數(shù),fun()是一個(gè)基類中的虛函數(shù),所以可以被晚綁定,調(diào)用子類中的函數(shù),從而實(shí)現(xiàn)了一個(gè)借口,多個(gè)函數(shù)的多態(tài)性.

  • 小結(jié)
    1. 如果有 virtual 才有多態(tài)(覆蓋基類函數(shù))
    2. 沒有多態(tài),按照原類型調(diào)用(隱藏)

純虛函數(shù):在基類中定義的虛函數(shù),沒有定義,派生類需要定義自己的實(shí)現(xiàn)方法.

virtual void function() = 0;

派生類中必須進(jìn)行重寫以實(shí)現(xiàn)多態(tài)性. 含有純虛函數(shù)的類成為抽象類,不能生成對(duì)象.

  • 編譯多態(tài)性: 通過重載實(shí)現(xiàn)
  • 運(yùn)行多態(tài)性: 通過虛函數(shù)實(shí)現(xiàn)(覆蓋)

虛函數(shù)表(vtable): 虛函數(shù)是通過虛函數(shù)表來(lái)實(shí)現(xiàn)的,這個(gè)表主要是一個(gè)類虛函數(shù)的地址表,這張表解決了繼承和多態(tài)的問題,保證了真實(shí)反映和使用實(shí)際的函數(shù).這個(gè)表在一個(gè)對(duì)象實(shí)例的最前面,我們可以通過變量虛函數(shù)表的函數(shù)指針,調(diào)用相應(yīng)的函數(shù).

class Base {
     public:
            virtual void f() { cout << "Base::f" << endl; }

            virtual void g() { cout << "Base::g" << endl; }

            virtual void h() { cout << "Base::h" << endl; }
};
Base b;

這個(gè)對(duì)象實(shí)例b的結(jié)構(gòu)如下:

圖片.png

  1. 一對(duì)一繼承(子類沒有覆蓋重寫虛函數(shù),這樣在實(shí)際中沒有意義,僅為對(duì)比)
    如果子類也有自己的虛函數(shù),并繼承父類的虛函數(shù)表,則其虛函數(shù)表的結(jié)構(gòu)為:


    圖片.png

    1)虛函數(shù)按照聲明順序放在表中
    2)父類的虛函數(shù)放在子類的前面

  2. 一對(duì)一繼承(有虛函數(shù)被覆蓋)
    父子兩個(gè)對(duì)象的表分別如下:


    圖片.png

則最后子類的虛函數(shù)表將為:


圖片.png

1)覆蓋的虛函數(shù),父類位置被子類頂替
2)其余順序不變
因此,如果有

Base *b = new Derive();
b->f();

該父類對(duì)象指向的地址是子類的地址,對(duì)象b將調(diào)用覆蓋后的 Derive::f().
這就是多態(tài)實(shí)現(xiàn)的原理.

  1. 多重繼承(無(wú)覆蓋)
    如果子類對(duì)父類的虛函數(shù)沒有覆蓋:


    圖片.png

    那么子類實(shí)例中的虛函數(shù)表是這樣子的:


    圖片.png

    1)每個(gè)父類有自己的虛表
    2)子類的虛函數(shù)在第一個(gè)父類之后
    3)父類順序是按照聲明順序來(lái)的

    當(dāng)每次設(shè)計(jì)到這些虛函數(shù)的時(shí)候,需要對(duì)應(yīng)到相應(yīng)的虛函數(shù)表(根據(jù)基類的定義),比如:

Base2 *ptr = new d();
ptr->f()  //調(diào)用第二個(gè)表的第一個(gè)函數(shù)(Base2 的 f())
  1. 多重繼承(有虛函數(shù)覆蓋)


    圖片.png

    所有相應(yīng)的同名虛函數(shù)都要被覆蓋:


    圖片.png

安全性

  • 子類中沒有重載的虛函數(shù)雖然出現(xiàn)在第一個(gè)虛函數(shù)表中,但是相應(yīng)的父類指針并不能訪問這個(gè)函數(shù).程序?qū)?bào)錯(cuò).但是仍然可以通過地址訪問的方式調(diào)用.
  • 對(duì)于父類中private的方法,如果是虛函數(shù),并被子類通過共有繼承,那么這個(gè)函數(shù)將出現(xiàn)在子類的虛函數(shù)表中,并可以通過地址訪問的方式被調(diào)用,這是很危險(xiǎn)的.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 多態(tài)(1)靜態(tài)多態(tài)與動(dòng)態(tài)多態(tài) 什么是多態(tài) 從字面上理解就是多種形態(tài)的意思。而多態(tài)一詞最初源自希臘語(yǔ),其含義便是“多...
    kingZXY2009閱讀 2,459評(píng)論 0 2
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 34,741評(píng)論 18 399
  • 繼承和多態(tài) 1. 繼承的優(yōu)缺點(diǎn) 優(yōu)點(diǎn):(1)子類可以靈活地改變父類中的已有方法;(2)能夠最大限度的實(shí)現(xiàn)代碼重用。...
    MinoyJet閱讀 729評(píng)論 0 0
  • 前言:一般查詢可以通過find方法,但如果是比較復(fù)雜的查詢或者數(shù)據(jù)統(tǒng)計(jì)的話,find可能就無(wú)能為力了,這時(shí)也許你需...
    n_ll閱讀 1,937評(píng)論 0 3
  • 若雪兒是城市里的普通女孩兒,快讀高一了。五官端正、皮膚白白凈凈的,尤其是眼睛,純潔美麗,長(zhǎng)得很漂亮。是那種讓...
    星雪薔薇閱讀 273評(píng)論 0 1

友情鏈接更多精彩內(nèi)容