2017年5月1日 星期一

繼承的虛擬函式與純虛擬函式的實際作用

繼承的虛擬函式與純虛擬函式的實際作用

tags: C++ Concept2
一句話形容虛擬函式的意思
該函式可有可無
這在繼承的時候起了一個極大的用處,基層類別的函式可有可無的特性會讓編譯器自動選擇正確的函式,稍後會繼續說明。

需要遵守的原則
總是讓你的Base擁有虛擬解構子
否則一個向上轉型的指針會被錯誤的解構

一句話形容純虛擬函式
禁止使用者操作你的父類別
你不會希望使用者可以操作你的父類別的,它只是用來定義或抽出各個類別相同處。

繼承

以下均是討論在繼承的情況下會遇到的問題
繼承的參考代碼
class Base{
public:
    Base(){}
    ~Base(){
        cout << "Base dtor" << endl;
    }
    void fun(){
        cout << "Base" << endl;
    }
};

class Drived: public Base{
public:
    Drived(){}
    ~Drived(){
        cout << "Drived dtor" << endl;
    }
    void fun(){
        cout << "Drived" << endl;
    }
};

函式多載

如果一個子類別加載了與父類別相同名稱的函式,那麼那個函式將會被覆蓋,子類別宣告的物件依子類別函式為主。
Base ba;
Drived dr;
ba.fun();
dr.fun();
結果會個別呼叫父類別與子類別的 fun()函式,如果子類別沒有宣告該函式則會繼承父類別的函式。

向上轉型

使用父類別指針來宣告子類別非常常見;有些時候也有必要進行安全的向上轉型(子類別指針轉成父類別),這種情況下,會出現一個問題
這個子類別會呼叫父類別的函式
試著在上述的代碼加入
Base* p = new Drived;
p->fun();
結果將呼叫父類別的 fun()函式
這可能不是我們要的結果,將fun()設置成虛擬函式,可以讓編譯器選擇正確的函式運行。
virtual void fun(){
    cout << "Base" << endl;
}
現在可以正確地呼叫子類別的 fun()函式 了。

不正確的解構

上述情況呼叫錯誤的函式這聽起來可能沒什麼事情,但是如果呼叫錯解構子可就有問題了。
Base* p = new Drived;
delete p;
這是一個未定義行為,讓一個子類別呼叫父類別的解構子,其結果可能導致記憶體遺失。必須為你的父類別的解構子加上 vitual,才能呼叫正確的解構子

不想讓使用者使用父類別

繼承最重要的目的之一就是抽出各個函式相同之處,集中寫到同一個類別內,再分別讓大家既成。
可問題就在於集中到的那個類別如果被使用者拿去使用宣告會發生什麼事情?可能不會發生什麼,不過你不會希望他被使用者拿去操作,因為這一點實用性都沒有。

抽象類別的定義

抽象類別指的是其成員函式至少具有一個純虛擬函式,純虛擬函式的寫法是另一個虛擬函式=0。
virtual void fun()=0;
抽象類別不能夠實際產生出一個物件,僅能夠被繼承。基於這一點可以完美的阻止使用者操作Base類別。

找不到適當的函式當作純虛擬函式?

還記得前面提到的總是讓你的Base擁有虛擬解構子嗎?我們可以從這裡下手,反正他都已經必須是了,並在代碼之後補上其定義,不讓使用者定義,可以完整的解決這個問題。
virtual ~Base()=0;
Base::~Base(){
    cout << "Base dtor" << endl;
}
現在使用者構不著你的Base類別了,並且你也不用為了純虛擬函式煩惱要怎麼生出一個新函式,對使用者而言也不需要自行補上純虛擬函式的定義。

範例代碼

/*****************************************************************
Name : 
Date : 2017/04/30
By   : CharlotteHonG
Final: 2017/04/30
*****************************************************************/
#include <iostream>
using namespace std;

class Base{
public:
    Base(){}
    virtual ~Base()=0;
    virtual void fun(){
        cout << "Base" << endl;
    }
};
Base::~Base(){
    cout << "Base dtor" << endl;
}

class Drived: public Base{
public:
    Drived(){}
    ~Drived(){
        cout << "Drived dtor" << endl;
    }
    void fun(){
        cout << "Drived" << endl;
    }
};
//================================================================
int main(int argc, char const *argv[]){
    Base* p = new Drived;
    delete p;
    return 0;
}
//================================================================

沒有留言:

張貼留言