[ZZ]c++ 覆蓋、重載、隱藏

原文鏈接:http://blog.csdn.net/besthyq/archive/2006/11/08/1373007.aspx

“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.h>

class Base

{

public:

void f(int x){ cout << "Base::f(int) " << x << endl; }

void f(float x){ cout << "Base::f(float) " << x << endl; }

virtual void g(void){ cout << "Base::g(void)" << endl;}

};

class Derived : public Base

{

public:

virtual void g(void){ cout << "Derived::g(void)" << endl;}

};

void main(void)

{

Derived d;

Base *pb = &d;

pb->f(42); // 運行結果: Base::f(int) 42

pb->f(3.14f); // 運行結果: Base::f(float) 3.14

pb->g(); // 運行結果: Derived::g(void)

}

函數Base::f(int)與Base::f(float)相互重載,而Base::g(void)被Derived::g(void)覆蓋。


隱藏是指派生類的函數屏蔽了與其同名的基類函數,規則如下:

1) 如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)

2) 如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)

比如,在下面的程序中:

#include <iostream.h>

class Base

{

public:

virtual void f(float x){ cout << "Base::f(float) " << x << endl; }

void g(float x){ cout << "Base::g(float) " << x << endl; }

void h(float x){ cout << "Base::h(float) " << x << endl; }

};

class Derived : public Base

{

public:

virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }//被繼承之後,virtual 可有可無,但最好有。繼承後,還是虛函數。

void g(int x){ cout << "Derived::g(int) " << x << endl; }

void h(float x){ cout << "Derived::h(float) " << x << endl; }

using Base::g;//這句話是用來引用父類中被隱藏的部分的。

};

通過分析可得:

1) 函數Derived::f(float)覆蓋了Base::f(float)。

2) 函數Derived::g(int)隱藏了Base::g(float),注意,不是重載。

3) 函數Derived::h(float)隱藏了Base::h(float),而不是覆蓋。

看完前面的示例,可能大家還沒明白隱藏與覆蓋到底有什麼區別,因爲我們前面都是講的表面現象,怎樣的實現方式,屬於什麼情況。下面我們就要分析覆蓋與隱藏在應用中到底有什麼不同之處。在下面的程序中bp和dp指向同一地址,按理說運行結果應該是相同的,可事實並非如此。

void main(void)

{

Derived d;

Base *pb = &d;

Derived *pd = &d;

// Good: behavior depends solely on type of the object

pb->f(3.14f); //運行結果: Derived::f(float) 3.14

pd->f(3.14f); //運行結果: Derived::f(float) 3.14

// Bad : behavior depends on type of the pointer

pb->g(3.14f); //運行結果: Base::g(float) 3.14

pd->g(3.14f); //運行結果: Derived::g(int) 3

// Bad : behavior depends on type of the pointer

pb->h(3.14f); //運行結果: Base::h(float) 3.14

pd->h(3.14f); //運行結果: Derived::h(float) 3.14

}

請大家注意,f()函數屬於覆蓋,而g()與h()屬於隱藏。從上面的運行結果,我們可以注意到在覆蓋中,用基類指針和派生類指針調用函數f() 時,系統都是執行的派生類函數f(),而非基類的f(),這樣實際上就是完成的“接口”功能。而在隱藏方式中,用基類指針和派生類指針調用函數f()時, 系統會進行區分,基類指針調用時,系統執行基類的f(),而派生類指針調用時,系統“隱藏”了基類的f(),執行派生類的f(),這也就是“隱藏”的由 來。


===================================================================
-------------------------------------------------------------------
-------------------------------------------------------------------

重載(overload)
這個好理解,在同個space域同名的.
參數必須不同,有關virtual無關.


覆蓋(override)
同名字,同參數,有virtual

覆蓋好理解比如show()函數
A派生了B
如果B中的show()覆蓋了A中的show()
但B中仍然有兩個show(),而不管是A類指針也好,B類對象調用也好,都只能調用B類自己的那個show();
而從A類繼承過來的show()函數真的就被覆蓋了,沒有了嗎? 答應是不對的.這時可以在B類對象顯示的調用A類繼承過來的show();

程序代碼:
#include <iostream>
using namespace std;
class A
{
public:
virtual void show()
{
cout << a << endl;
}

int a;
};
class B:public A
{
public:
void show()
{
A::show();
//顯式地調用自己類中的 "由A類繼承過來的show()函數"
//像這種直接顯式指出某個類的某個函數時,
//編譯器處理方式是這樣的: 首先在自己類中找有沒有A::show(),如果找到,調用.不在繼續在A類中找
//如果找不到,則在顯式指出的那個類中(即A類)調用那個函數. 這裏當然是在B類中可以找到A::show()
//因爲基類中指出了這個函數是virtual函數.
}
int b;
};

int main()
{
A a;
a.a = 3;
a.show();
B b;
b.b = 4;
b.show();
b.A::show(); //顯示的調用自己類中的 "由A類繼承過來的show()函數"
return 0;
}

總結:
virtual, 覆蓋.
其實準確,通俗的講B類還是有兩個show(),只是調用由A繼承過來的show()只能通過顯式的調用方法 [類名::virtual函數名]
而不管是基類A的指針 (B b; A *p = &b; p->show())或者派生類的對象(B b; b.show()),都只能調用B類的自己本身存在的show()函數

-----------------------------------------------------------------------------------------------------------------------

隱藏hide
這個有點麻煩了...
什麼是隱藏情況?
1,同名同參無virtual
2,同名不同參不管有無virtual

程序代碼:
class A
{
public:
void show() {}; //編號1
void rose(int a) {} //編號2
};
class B:public A
{
public:
void show() {}; //編號3
void rose(int a, int b) {}; //編號4
};
//
類B中的show()和rose()明顯是隱藏了類A的show()和rose()
隱藏的理解:
B類中其實有兩個show(),兩個rose();
但爲什麼不叫重載呢?你會這樣想,但我可以告訴你,因爲類B中的兩個show(),兩個rose(),不是都可以被類B對象調用的.

//----1----//
編號1和編號2,在類B中哪怕存在,但只能通過類A的指針調用,而不能通過B類對象調用,如:


程序代碼:
A *p = new B;
p->show();
p->rose(3);
p->rose(3,5); //error

//----2----//
編號3和編程4,只能通過類B對象調用,而不能通過類A的指針調用,如:


程序代碼:
B b;
b.show();
b.rose(3,5);
b.rose(4); //error

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章