重載與覆蓋的區別

(轉自 http://blog.csdn.net/xxxluozhen/article/details/4030946ios

一、方法的覆蓋是子類和父類之間的關係,是垂直關係;方法的重載是同一個類中方法之間的關係,是水平關係。
二、覆蓋只能由一個方法,或只能由一對方法產生關係;方法的重載是多個方法之間的關係。
三、覆蓋要求參數列表相同;重載要求參數列表不一樣。
四、覆蓋關係中,調用那個方法體,是根據對象的類型(對象對應存儲空間類型)來決定;重載關係,是根據調用時的實參表與形參表來選擇方法體的。
override能夠翻譯爲覆蓋,從字面就能夠知道,它是覆蓋了一個方法而且對其重寫,以求達到不一樣的做用。對咱們來講最熟悉的覆蓋就是對接口方法的實現,在接口中通常只是對方法進行了聲明,而咱們在實現時,就須要實現接口聲明的全部方法。除了這個典型的用法之外,咱們在繼承中也可能會在子類覆蓋父類中的方法。在覆蓋要注意如下的幾點:
    一、覆蓋的方法的標誌必需要和被覆蓋的方法的標誌徹底匹配,才能達到覆蓋的效果;
    二、覆蓋的方法的返回值必須和被覆蓋的方法的返回一致;
    三、覆蓋的方法所拋出的異常必須和被覆蓋方法的所拋出的異常一致,或者是其子類;
    四、被覆蓋的方法不能爲private,不然在其子類中只是新定義了一個方法,並無對其進行覆蓋。
     overload對咱們來講可能比較熟悉,能夠翻譯爲重載,它是指咱們能夠定義一些名稱相同的方法,經過定義不一樣的輸入參數來區分這些方法,而後再調用時,VM就會根據不一樣的參數樣式,來選擇合適的方法執行。在使用重載要注意如下的幾點:
    一、在使用重載時只能經過不一樣的參數樣式。例如,不一樣的參數類型,不一樣的參數個數,不一樣的參數順序(固然,同一方法內的幾個參數類型必須不同,例如能夠是fun(int, float), 可是不能爲fun(int, int));
    二、不能經過訪問權限、返回類型、拋出的異常進行重載;
    三、方法的異常類型和數目不會對重載形成影響;
   overload編譯時的多態   
   override運行時的多態
面向對象程序設計中的另一個重要概念是多態性。在運行時,能夠經過指向基類的指針,來調用實現派生類中的方法。能夠把一組對象放到一個數組中,而後調用它們的方法,在這種場合下,多態性做
用就體現出來了,這些對象沒必要是相同類型的對象。固然,若是它們都繼承自某個類,你能夠把這些派生類,都放到一個數組中。若是這些對象都有同名方法,就能夠調用每一個對象的同名方法。
同一操做做用於不一樣的對象,能夠有不一樣的解釋,產生不一樣的執行結果,這就是多態性。多態性經過派生類重載基類中的虛函數型方法來實現。
在面向對象的系統中,多態性是一個很是重要的概念,它容許客戶對一個對象進行操做,由對象來完成一系列的動做,具體實現哪一個動做、如何實現由系統負責解釋。
「多態性」一詞最先用於生物學,指同一種族的生物體具備相同的特性。在C#中,多態性的定義是:同一操做做用於不一樣的類的實例,不一樣的類將進行不一樣的解釋,最後產生不一樣的執行結果。C#支持兩種類型的多態性:
● 編譯時的多態性
編譯時的多態性是經過重載來實現的。對於非虛的成員來講,系統在編譯時,根據傳遞的參數、返回的類型等信息決定實現何種操做。
● 運行時的多態性
運行時的多態性就是指直到系統運行時,才根據實際狀況決定實現何種操做。C#中,運行時的多態性經過虛成員實現。
編譯時的多態性爲咱們提供了運行速度快的特色,而運行時的多態性則帶來了高度靈活和抽象的特色。
舉個簡單的例子:   
   void    test(CBase    *pBase)   
   {   
       pBase->VirtualFun();   
   }   
    
   這段程序編譯的時刻並不知道運行時刻要調用那個子類的函數,因此編譯的時刻並不會選擇跳轉到那個函數去!若是不是虛函數,那麼跳轉的僞彙編代碼應該是call    VirtuallFun!但當是虛函數的時候,就不能這樣了,而是變成了call    pBase->虛函數表裏的一個變量,不一樣的子類在這個變量含有不一樣的函數地址,這就是所謂的運行時刻了。但事實上    pBase->虛函數表裏的一個變量    也是在編譯時刻就產生的的,它是固定的。    因此運行時刻,仍是編譯時刻事實上也並不嚴密,重要的仍是理解它的實質!
虛函數只是一個函數指針表,具體調用哪一個類的相關函數,要看運行是,對象指針或引用所指的真實類型,因爲一個基類的指針或引用能夠指向不一樣的派生類,因此,當用基類指針或引用調用虛函數時,結果是由運行時對象的類型決定的數組

 #################################################################################ide

「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;spa

  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;
  .net

  // 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(),這也就是「隱藏」的由來。設計

相關文章
相關標籤/搜索