1. 程式人生 > >C++ 覆蓋,過載,隱藏

C++ 覆蓋,過載,隱藏

“overload”翻譯過來就是:超載,過載,過載,超出標準負荷;“override”翻譯過來是:重置,覆蓋,使原來的失去效果。

先來說說過載的含義,在日常生活中我們經常要清洗一些東西,比如洗車、洗衣服。儘管我們說話的時候並沒有明確地說用洗車的方式來洗車,或者用洗衣服的方式來洗一件衣服,但是誰也不會用洗衣服的方式來洗一輛車,否則等洗完時車早就散架了。我們並不要那麼明確地指出來就心知肚明,這就有過載的意思了。在同一可訪問區內被聲名的幾個具有不同引數列的(引數的型別、個數、順序不同)同名函式,程式會根據不同的引數列來確定具體呼叫哪個函式,這種機制叫過載,過載不關心函式的返回值型別。這裡,“過載”的“重”的意思不同於“輕重”的“重”,它是“重複”、“重疊”的意思。例如在同一可訪問區內有:

① 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,①②③④⑤⑥中任兩個均構成過載,⑥和⑦也能構成過載,而①和⑦卻不能構成過載,因為①和⑦的引數相同。

覆蓋是指派生類中存在重新定義的函式,其函式名、引數列、返回值型別必須同父類中的相對應被覆蓋的函式嚴格一致,覆蓋函式和被覆蓋函式只有函式體(花括號中的部分)不同,當派生類物件呼叫子類中該同名函式時會自動呼叫子類中的覆蓋版本,而不是父類中的被覆蓋函式版本,這種機制就叫做覆蓋。

下面我們從成員函式的角度來講述過載和覆蓋的區別。

成員函式被過載的特徵有:

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; }

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

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

};

通過分析可得:

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