C++類物件中虛擬函式與多型性的實現
在面向物件程式設計時,有時會遇到這種需求:我們希望同一個方法在基類和派生類中實現不同的功能,即體現出行為上的多型性。一般有兩種方法可以實現這種需求,其一是在派生類中重新定義基類中方法,其二是使用虛擬函式。這裡主要記錄利用虛擬函式實現多型性的方法。
類中虛擬函式的定義方法
虛擬函式使用關鍵詞virtual進行標識。通過一個例子加深對虛擬函式的理解,一個銀行需要開發兩類賬戶,一類是基本賬戶BaseAcct,另一類是高階賬戶PlusAcct,具有透支功能,多顯示透支上限和透支額度資訊。兩個賬戶的基本特徵為:
class BaseAcct{
private:
string fullName;
long balance;
public:
BaseAcct(const stirng &s="Nullbody", long bal=0.0);
void Deposit(double amt); //存款
virtual void Withdraw(double amt); //取款,無透支功能
virtual void ViewAcct() const;
virtual ~BaseAcct() {}; //基類解構函式最好定義為虛擬函式以保證正確的析構順序
};
class PlusAcct:public BaseAcct{
private:
double maxLoan; //透支上限和透支額度
double owesBank;
public:
PlusAcct(const stirng &s="Nullbody", long bal=0.0, double ml=500, double ob=0.0);
virtual void Withdraw(double amt); //取款,有透支功能
virtual void ViewAcct() const; //多顯示透支上限和透支額度
~PlusAcct() {}; //解構函式是自帶預設的,可以不寫
};
派生類PlusAcct中對基類BaseAcct中的虛擬函式Withdraw()和ViewAcct()進行改寫,實現了不同的功能。可以在後續進行重寫,這是虛擬函式的特點。但是重寫時要注意,重寫的函式其函式原型要保持與基類中完全相同,否則編譯器將會產生warning。只有返回值可以由基類指標/引用改變為派生類指標/引用,稱之為返回值協變。
上述特點與過載不同,過載中要求函式的引數列表必須不同才能實現過載。
虛擬函式的工作原理
編譯器在處理虛擬函式的方法是為每一個物件增加一個隱藏成員,其中儲存了一組指向虛擬函式陣列地址的指標,一般稱之為虛擬函式表。
動態聯編與多型性
在管理BaseAcct和PlusAcct帳戶時,不能用一個數組去儲存BaseAcct和PlusAcct物件,因為兩者的型別不同。但是卻可以建立指向BaseAcct類的指標陣列,然後根據需要讓其指向BaseAcct或PlusAcct物件。因為編譯器在處理虛擬函式時是採用動態聯編的,即在程式執行的時候再選擇正確的虛方法。而這種動態聯編的特性,可以讓同一個陣列中的指標指向不同的基類或者派生類,並讓其表現出不同的特性,即為多型性。
通過指標或者引用呼叫方法時,若為virtual,將根據指標或者引用指向物件選擇方法;若非virtual,將根據指標或者引用本身的型別選擇方法。這個特性是類物件實現多型性的基礎。
祝楓
2018年10月9日