過載、覆蓋和隱藏的區別
“overload”翻譯過來就是:超載,過載,過載,超出標準負荷;“override”翻譯過來是:重置,覆蓋,使原來的失去效果。
先來說說過載(Overload)的含義,在日常生活中我們經常要清洗一些東西,比如洗車、洗衣服。儘管我們說話的時候並沒有明確地說用洗車的方式來洗車,或者用洗衣服 的方式來洗一件衣服,但是誰也不會用洗衣服的方式來洗一輛車,否則等洗完時車早就散架了。我們並不要那麼明確地指出來就心知肚明,這就有過載的意思了。在 同一可訪問區內被聲名的幾個具有不同引數列的(引數的型別、個數、順序不同)同名函式,程式會根據不同的引數列來確定具體呼叫哪個函式,這種機制叫過載, 過載不關心函式的返回值型別。這裡,“過載”的“重”的意思不同於“輕重”的“重”,它是“重複”、“重疊”的意思。例如在同一可訪問區內有:
① double calculate(double);
② double calculate(double,double);
③ double calculate(double, int);
④ double calculate(int, double);
⑤ double calculate(int);
⑥ float calculate(float);
⑦ float calculate(double);
六個同名函式calculate,①②③④⑤⑥中任兩個均構成過載,⑥和⑦也能構成過載,而①和⑦卻不能構成過載,因為①和⑦的引數相同。
函式過載:兩個函式的形式引數必須不同(比如引數型別、引數個數和引數順序,這三者中必須至少有一個不同),而函式的返回值型別可以相同也可以不相同,也就是說函式的返回值型別與函式是否發生過載是沒有關係的。
覆蓋(Override)是指派生類中存在重新定義的函式,其函式名、引數列、返回值型別必須同父類中的相對應被覆蓋的函式嚴格一致,覆蓋函式和被覆蓋函式只有函式體 (花括號中的部分)不同,當派生類物件呼叫子類中該同名函式時會自動呼叫子類中的覆蓋版本,而不是父類中的被覆蓋函式版本,這種機制就叫做覆蓋。
下面我們從成員函式的角度來講述過載和覆蓋的區別。
成員函式被過載的特徵有:
1) 相同的範圍(在同一個類中);
2) 函式名字相同;
3) 引數不同;
4) virtual關鍵字可有可無。
覆蓋的特徵有:
1) 不同的範圍(分別位於派生類與基類);
2) 函式名字相同;
3) 引數相同;
4) 基類函式必須有virtual關鍵字。
比如,在下面的程式中:
#include <iostream> using namespace std; class A { public: void f(int x){cout<<"A::f(int) "<<x<<endl;} void f(float x){cout<<"A::f(float) "<< x<<endl;} virtual void g(void){cout<< "A::g(void)"<<endl;} }; class B : public A { public: virtual void g(void){cout<<"B::g(void)"<<endl;} }; int main(void) { B d; A *p = &d; p->f(42); // 執行結果:A::f(int) 42 p->f(3.14f); // 執行結果:A::f(float) 3.14 d.g();// 執行結果:B::g(void) p->g(); // 執行結果:B::g(void) /* 執行結果顯示: A::f(int) 42 A::f(float) 3.14 B::g(void) B::g(void) 請按任意鍵繼續. . . */ return 0; }
函式A::f(int)與A::f(float)相互過載,而A::g(void)被B::g(void)覆蓋。
隱藏是指派生類的函式遮蔽了與其同名的基類函式,規則如下:
1) 如果派生類的函式與基類的函式同名,但是引數不同。此時,不論有無virtual關鍵字,基類的函式將被隱藏(注意別與過載混淆)。
2) 如果派生類的函式與基類的函式同名,並且引數也相同,但是基類函式沒有virtual關鍵字。此時,基類的函式被隱藏(注意別與覆蓋混淆)。
比如,在下面的程式中:
#include <iostream>
using namespace std;
class A
{
public:
virtual void f(float x){cout<<"A::f(float) "<<x<<endl;}//虛擬函式
void g(float x){cout<<"A::g(float) "<<x<<endl;}
void h(float x){cout<<"A::h(float) "<<x<<endl;}
};
class B : public A
{
public:
virtual void f(float x){cout<<"B::f(float) "<<x<<endl;}//虛擬函式
void g(int x){cout<<"B::g(int) "<<x<<endl;}
void h(float x){cout<<"B::h(float) "<<x<<endl;}
using A::g;//這句話是用來引用父類中被隱藏的部分的。
};
int main(void)
{
B test1;
A *p = &test1;
test1.g(99);//B::g(int) 99
test1.h(88.8);//B::h(float) 88.8
//下面顯示了虛擬函式的功能
test1.f(66.6);//B::f(float) 66.6
p->f(55.5);//B::f(float) 55.5
A test2;
p = &test2;
p->f(33.3);//A::f(float) 33.3
/*
輸出結果是:
B::g(int) 99
B::h(float) 88.8
B::f(float) 66.6
B::f(float) 55.5
A::f(float) 33.3
請按任意鍵繼續. . .
*/
return 0;
}
通過分析可知:
(1. 函式B::g(int)隱藏了A::g(float),注意,不是過載。
(2. 函式B::h(float)隱藏了A::h(float),注意,不是覆蓋。
(3. 函式B::f(float)覆蓋了A::f(float),有virtual。
看完前面的示例,可能大家還沒明白隱藏與覆蓋到底有什麼區別,因為我們前面都是講的表面現象,怎樣的實現方式,屬於什麼情況。下面我們就要分析覆蓋與隱藏在應用中到底有什麼不同之處。在下面的程式中bp和dp指向同一地址,按理說執行結果應該是相同的,可事實並非如此。
int main(void) { B b; A *p_a = &b; B *p_b = &b; //good:behavior依賴於物件的型別 p_a->f(3.14f); p_b->f(3.14f); //bad:bahavior依賴於指標型別 p_a->g(3.14f); p_b->g(3.14f); //bad:behavior依賴於指標型別 p_a->h(3.14f); p_b->h(3.14f); /* B::f(float) 3.14 B::f(float) 3.14 A::g(float) 3.14 B::g(int) 3 A::h(float) 3.14 B::h(float) 3.14 請按任意鍵繼續. . . */ return 0; }