C++函數重載、隱藏與覆蓋重寫的精通指南

前言

對於C++函數而言,多個函數如果同名會有很多有意思的事情,從聲明的作用域來看,在橫向上同一個可訪問作用域裡面的同名函數可以進行重載;而縱向上作用域對於父子繼承的派生類來說,同樣的函數名稱可以實現隱藏與覆蓋。(如果基類成員函數是虛函數,可以基於虛函數實現多態,進行動態聯編)下面就詳細介紹下函數的重載、隱藏與覆蓋重寫。

1 函數重載

  • 定義:

C++規定在同一作用域中,例如一個類的成員函數之間,多個函數的名稱相同,但是各個函數的形式參數(指參數的個數、類型或者順序)不同時,構成函數重載。

  • 代碼示例
int test(int a);
int test(int a, double b);
int test(double b, int a);
int test(int a, const char ** c); 
void test(int a, const char ** c); 		  // 非重載,一起編譯會提示錯誤,僅僅返回值不同編譯無法區分使用的是那個重載函數
  • 總結
  • 前提:函數名稱相同,即要求是同名函數;
  • 重載作用域:函數重載發生在橫向水平的同一作用域,例如一個類成員函數之間的重載、全局函數之間的重載;
  • 重載類型:無論是類的靜態成員函數,還是類的普通成員函數,亦或是普通的函數,都可以形成重載;
  • 重載要素:函數返回值類型函數重載無任何關系,僅僅返回值不同,形參相同的情況,會被禁止重載;

2 函數隱藏

  • 定義

函數隱藏是說,在不同作用域中,定義的同名函數構成函數隱藏(僅僅要求函數名稱相同,對於返回值和形式參數不做更多要求,並且對於是否是虛函數也不做要求)。例如派生類同名成員函數屏蔽與其基類的同名成員函數,以及屏蔽同名全局外部函數。(經常有人隱藏和覆蓋重寫弄混,所以提前說下,如果在派生類中存在與基類同名的虛函數,並且返回值、形參都相同,則構成函數重寫)。

  • 代碼示例
#include <iostream>

using namespace std;

class Parent
{
  public:
    void test(int a) {
      cout<<"this is Parent"<<endl;
    }
};

class Son: public Parent
{
  public:
    void test(int a) {
      cout<<"this is Son hide Parent function"<<endl;
    }
};

int main(int argc, char ** argv) {
  Son son;
  son.test(1);
  return 0;
}

​ 輸出如下

root@localhost override [master] $ g++ --std=c++11 test_hide.cpp
root@localhost override [master] $ ./a.out
this is Son hide Parent function
  • 總結
  • 前提:函數名稱相同,即要求是同名函數;
  • 作用域:不在同一個橫向的作用域(分別位於派生類與基類的縱向作用域);
  • 要素:返回類型可同可不同,參數亦可同可不同;
  • 虛函數:

    參數不同,此時無論有無virtual關鍵字,基類的函數將被隱藏;

    參數相同的情況下

    此時基類函數無virtual則屬於函數隱藏,後續無法繼續基於此利用這個函數的多態性;

    如果是virtual則屬於函數重寫,繼續多態性的保留;

3 函數重寫

  • 定義

函數的覆蓋和重寫是一個意思的兩個叫法,同時他的作用域也和函數隱藏相同,其實可以這麼看,函數覆蓋和函數隱藏共同構建瞭在具有集成關系的縱向作用域裡面的同名函數的不同衍變,隻不過函數覆蓋的條件更加嚴格些。

在介紹函數隱藏的時候,為瞭弄清楚函數隱藏與覆蓋重寫,也簡單描述瞭函數覆蓋。這裡再進一步進行描述下:派生類中與基類中,同名函數的返回值類型、參數的都相同,並且基類中定義為虛函數的情況下,構成虛函數覆蓋,也叫虛函數重寫。

  • 代碼示例
#include <iostream>
using namespace std;
class Parent
{
  public:
    virtual void test(int a) {
      cout<<"this is Parent"<<endl;
    }
};

class Son: public Parent
{
  public:
    void test(int a) {
      cout<<"this is Son Override Parent function"<<endl;
    }
};

int main(int argc, char ** argv) {
  Son son;
  son.test(1);
  return 0;
}

輸出如下:

root@localhost override [master] $ g++ --std=c++11 test_override2.cpp
root@localhost override [master] $ ./a.out
this is Son Override Parent function

附:令人迷惑的隱藏規則

C++的隱藏規則使問題復雜性陡然增加,這裡“隱藏”是指派生類的函數屏蔽瞭與其同名的基類函數,規則如下:

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

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

總結

咋一看,感覺重寫的功能基於隱藏是都可以實現,那麼為什麼要區分重寫和隱藏呢?其實這是C++語言層面的問題瞭,C++基於virtual函數實現瞭多態性,並且可以進行動態聯編,但是隱藏其實是破壞瞭這種多態性,也就是說父類成員函數的virtual性,在被子類成員函數的隱藏破壞後,無法傳遞給孫子類瞭,所以還需要重寫來遺產的傢族傳遞。

  • 前提:函數名稱相同,即要求是同名函數;
  • 作用域:不在同一個橫向的作用域(分別位於派生類與基類的縱向作用域);
  • 要素:返回類型可同可不同,參數亦可同可不同;
  • 是否虛函數:前提是虛函數,並且參數相同;

到此這篇關於C++函數重載、隱藏與覆蓋重寫的文章就介紹到這瞭,更多相關C++函數重載、隱藏與覆蓋重寫內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: