重寫必須滿足的條件:
1. 基類中的函數使用virtual修飾,即基類中的函數為虛函數;
2. 衍生類別中與基類中的這個virtual函數要有相同的函數原型(即:傳回型別相同、函數名相同、形參列表相同);
下面我們看個簡單的程式:
#include <iostream>using std::cout;using std::endl;class Person{public: virtual void Study() { cout << "這是基類中的方法" << endl; }};class Man : public Person{public: virtual void Study() { cout << "這是衍生類別中的方法" << endl; }};int main(void){ Person *p = new Man; p -> Study();// 輸出結果:這是衍生類別中的方法 return 0;}
Man類中的Study()函數對Person類中的Study()函數進行了重寫。
也許有些人認為:傳回型別可以不同,這個我們可以做個測試,就是將Man類中的Study()方法改為傳回值為int類型:
virtual void Study(){cout << "這是衍生類別中的方法" << endl;return 0;}
再次編譯,我們會發現,程式編譯都不能通過,提示傳回型別衝突,重寫錯誤。
所以,重寫(Override)或覆蓋只會發生在多態中。對於基類中的非virtual函數,在衍生類別中重新定義只會導致隱藏。
隱藏:指的是衍生類別中的函數屏蔽了基類中的函數。
隱藏的規則:
1. 對於基類中的非virtual函數,如果衍生類別中存在和基類中相同的函數名,而不管傳回值和形參列表是否相等;都會導致基類中的所有同名函數被衍生類別隱藏;
2. 對於基類中的virtual函數,如果衍生類別中存在和基類中相同的函數名,而且形參列表不同,就會導致基類中的所有同名的virtual函數被衍生類別隱藏;
注意:如果衍生類別中的函數和基類中的同名的virtual函數具有相同的形參列表和不同的傳回值,那麼將不會隱藏基類中的函數,直接導致編譯錯誤(原因:編譯器此時會認為你是在重寫方法,而不是隱藏,只是傳回型別寫錯了)。
下面我們看一個簡單的程式:
#include <iostream>using std::cout;using std::endl;class Person{public: void Eat() { cout << "這是基類中的Eat方法" << endl; } void Work() { cout << "這是基類中不帶參數、傳回值為void的Work函數" << endl; } void Work(int a) { cout << "這是基類中帶一個參數、傳回值為void的Work函數" << endl; } virtual void Study() { cout << "這是基類中不帶參數的虛方法" << endl; } virtual void Study(int a) { cout << "這是基類中有一個參數的虛方法" << endl; }};class Man : public Person{public: int Work() { cout << "這是衍生類別中不帶參數、傳回值為int的Work函數" << endl; return 0; } virtual void Study(int a, int b) { cout << "這是衍生類別中虛的方法" << endl; }};int main(void){ Man m; m.Eat(); // m.Study(); //該函數已被衍生類別中定義的Study(int a, int b)隱藏,調用會導致編譯錯誤 // m.Study(1); //該函數已被衍生類別中定義的Study(int a, int b)隱藏,調用會導致編譯錯誤 m.Study(1, 2); m.Work(); // m.Work(1); //該函數已被衍生類別隱藏中定義的Work()隱藏,調用會導致編譯錯誤 return 0;}
分析:在衍生類別中,沒有定義Eat()方法,所以衍生類別可以正常的調用基類中的Eat()方法,但是後面的Study()方法,在衍生類別中對其進行了定義,雖然函數原型不同,但也隱藏了基類中的所有名為Study()的函數,需要注意的是:如果衍生類別中的函數和基類中的同名的virtual函數具有相同的形參列表和不同的傳回值,那麼將不會隱藏基類中的函數,直接導致編譯錯誤(原因:編譯器此時會認為你是在重寫方法,而不是隱藏,只是傳回型別寫錯了)。關於這一點,你可以在Man中添加一個int
Study()函數試一下。對於Work()函數,同樣是由於在衍生類別中定義了一個同名的函數,導致基類中重載的一組Work()函數都被屏蔽。
對於隱藏,我們從字面意思也可以看出,只是將基類中的同名的函數屏蔽了,並沒有將其銷毀,所以我們還是可以對其進行調用的。
主要有以下三種方法:
1. 在衍生類別中的函數中調用基類中的同名函數,比如:
virtual void Study() { Person::Study(); }
2. 在衍生類別中定義函數的前面使用using關鍵字顯式地聲明;
using Person::Study; virtual void Study() { cout << "這是基類中不帶參數的虛方法" << endl; }
3. 採用衍生類別對象.基類名::基類中的方法;直接調用;例如:對於上面的程式,如果我們要調用基類中的Study()方法,我們可以這樣才調用:m.Person::Study();
通過這三種方式,我們就可以訪問被衍生類別屏蔽了的基類中定義的函數。
總結:所謂重寫(或叫覆蓋),指的是衍生類別對基類中的虛函數進行方法體的改寫(要求函數原型相同);而隱藏指的是,衍生類別中的函數屏蔽了基類中同名的所有函數。(對virtual函數,要求函數名相同,形參列表不同;對非virtual函數,函數名相同即可)。注意:在繼承中不涉及重載的概念,因為重載最基本的要求是:在相同的範圍中,而繼承顯然屬於不同範圍。