天天看點

繼承與多态---上 --- 函數重寫、多态、虛函數、覆寫、隐藏

函數重寫

函數重寫:在子類中定義與父類中原型相同的函數。

函數重寫隻發生在父類與子類之間。

父類中被重寫的函數依然會繼承給子類。

預設情況下子類中重寫的函數将隐藏父類中的函數。程式會優先調用子類中的同類型函數。

通過 作用域分辨符:: 可以通路到父類中被隐藏的函數。

如下圖所示:

繼承與多态---上 --- 函數重寫、多态、虛函數、覆寫、隐藏

下面調用這兩個prinft函數:

繼承與多态---上 --- 函數重寫、多态、虛函數、覆寫、隐藏

當函數重寫遇上指派相容性原則:

問題所在:

1、C++與C相同,是靜态編譯型語言。即編譯時變量和類型必須非常清晰。

2、在編譯時,編譯器自動根據指針的類型判斷指向的是一個什麼樣的對象。

3、是以編譯器認為父類指針指向的是父類對象(根據指派相容性原則,這個假設合理)。

4、由于編譯的時候程式沒有運作,是以不可能知道父類指針指向的具體是父類對象還是子類對象。

5、從程式安全的角度,編譯器假設父類指針隻指向父類對象,是以編譯的結果為調用父類的成員函數,編譯器認為最安全的做法是編譯到父類的print函數。

下圖所示:

繼承與多态---上 --- 函數重寫、多态、虛函數、覆寫、隐藏

上面的程式中,後面兩個print都是父類中的函數,輸出的内容都是I’m Parent,因為編譯器把後面兩個綁定到父類的成員函數上。

在編譯這個函數的時候,編譯器不可能知道指針 pp 究竟指向了什麼。但是編譯器沒有理由報錯。于是,編譯器認為最安全的做法是編譯到父類的print函數。

多态的本質

面向對象的新需求:

1、根據實際的對象類型來判斷重寫函數的調用,而不是像上面那樣編譯器調用父類的函數。

2、如果父類指針指向的是父類對象則調用父類中定義的函數。

3、如果父類指針指向的是子類對象則調用子類中定義的重寫函數。

繼承與多态---上 --- 函數重寫、多态、虛函數、覆寫、隐藏

虛函數

C++中的多态支援:

C++中通過virtual關鍵字對多态進行支援。

使用virtual聲明的函數被重寫後即可展現多态特性。

當需要根據實際的對象類型來判斷調用的是父類的重寫函數還是子類的重寫函數時,需要使用虛函數,在父類和子類的重寫函數前加上virtual關鍵字,在父類的重寫函數前加上了virtual,子類的可以省略。

基類的成員函數設為virtual,其派生類的相應的函數也會自動變為虛函數。

舉例說明:

#include <cstdlib>
#include <iostream>

using namespace std;

class Boss
{
private:
    static Boss* cInstance;   //單例對象 
    Boss()
    {
    }
public:
    static Boss* GetInstance()
    {
        if( cInstance == NULL )
        {
             cInstance = new Boss();
        }
        return cInstance;
    }
    int fight()
    {
        cout<<"Boss::fight()"<<endl;
        return ;
    }
};

Boss* Boss::cInstance = NULL;//初始化 

class Master
{
public:
    virtual int eightSwordKill()//虛函數 
    {
        cout<<"Master::eightSwordKill()"<<endl;
        return ;
    }
};

class NewMaster : public Master
{
public:
    virtual int eightSwordKill()//虛函數 
    {
        cout<<"NewMaster::eightSwordKill()"<<endl;
        return Master::eightSwordKill() * ;
    }
};

void fieldPK(Master& master, Boss* boss)
{
    int k = master.eightSwordKill();
    int b = boss->fight();

    if( k < b )
    {
        cout<<"Master is killed..."<<endl;
    }
    else
    {
        cout<<"Boss is killed..."<<endl;
    }
    cout<<endl;
}

int main(int argc, char *argv[])
{
    Boss* boss = Boss::GetInstance();

    cout<<"Master vs Boss"<<endl;
    Master master;
    fieldPK(master, boss);

    cout<<"New Master vs Boss"<<endl;   
    NewMaster newMaster;
    fieldPK(newMaster, boss);

    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}
           
繼承與多态---上 --- 函數重寫、多态、虛函數、覆寫、隐藏

小結

函數重寫是面向對象中很可能發生的情形。

函數重寫隻可能發生在父類與子類之間。

需要根據實際對象的類型确定調用的具體函數。

virtual關鍵字是C++中支援多态的唯一方式。

被重寫的虛函數即可表現出多态的特性。

繼續閱讀