1. 程式人生 > >C++中的過載和覆蓋,還有(隱藏)

C++中的過載和覆蓋,還有(隱藏)

前言

過載(overload)和覆蓋(override)是C++中關於函式的兩個基礎概念,但是如果讓你說出他們具體的描述和區別,一下子還真是不太容易說的很清楚和全面,這裡簡單把記錄一下,作為備忘。關於隱藏我覺得是個誤解,C++中根本沒有隱藏的說法和規則。

過載

過載(overload)是指同一個作用域內幾個函式名字相同,但形參列表不同(包括引數個數、型別、引數順序)。注意:函式返回值不是作為區分過載函式的標誌。

定義過載函式

定義以下幾個函式:

void print(const char *cp);
void print(const int *beg, const int *end);
void print(const int ia[], size_t size);

這些函式的名字一樣,形參個數和型別不一樣。當呼叫這些函式時,編譯器會根據傳遞的實參型別推斷呼叫的是哪個函式:

int ary[2] = {0, 1};
print("Hello world");           // 呼叫print(const char *)
print(j, end(ary) - begin(ary));// 呼叫print(const int, size_t)
print(begin(ary), end(ary));    // 呼叫print(const int *, const int *)

兩個函式除了返回型別不同,其他所有要素都相同的話,第二個函式的宣告是錯誤的,例如:

void print(const char *cp);
bool print(const char *cp); // 錯誤

判斷兩個形參的型別是否相異

有些函式的形參列表看起來不一樣,實際上是一樣的:

Record lookup(const Account &acct);
Record lookup(const Account&);

typedef Phone Telno;
Record lookup(const Phone&);
Record lookup(const Telno&);

第一對宣告中,在函式的宣告中形參沒有實質的含義,有沒有它不會影響形參列表的內容。
第二對宣告,typedef是已存在的型別提供了另外一個名字,並不建立新的型別,因此,第二對中兩個函式本質上沒有不同。

過載和const形參

頂層const(top-level const) 底層const(low-level const)
-- 頂層const和底層const的概念參見《C++ Primer》第五版 2.4.3小節

頂層const不影響傳入函式的物件。一個擁有頂層const的形參無法和另一個沒有頂層const的形參區分開來。

Record lookup(Phone);
Record lookup(const Phone);  // 重複宣告

Record lookup(Phone*);
Record lookup(Phone* const); // 重複宣告

上面兩組函式宣告,每一組的兩個都是等價的。

Record lookup(Accout&);
Record lookup(const Accout&);  // 新函式

Record lookup(Accout&*);
Record lookup(const Accout&); // 新函式

上面兩組例子中,編譯器可以通過實參是否是常量來推斷應該呼叫哪個函式。因為const不能轉換成其他型別,所以我們只能把const物件(或指向const的指標)傳遞給const形參。相反,非常量可以轉換成const,所以上面的4個函式都能作用於非常量物件或者指向非常量物件的指標。當我們傳遞一個非常量物件或者非常量物件的指標時,編譯器會優先選用非常量版本的函式。

cosnt_cast和過載

const_cast可以用在過載函式中。例如下面這個函式:

const string &shorterString(const string &s1, const string &s2)
{
    return s1.size() <= s2.size() ? s1 : s2;
}

這個函式的引數和返回型別都是const string的引用。我們可以對兩個非常量的string實參呼叫這個函式,但返回的結果仍然是const string的引用。因此我們需要一種新的shorterString函式,當它 的實參不是常量時,得到的結果是一個普通的引用,使用const_cast可以做到這一點:

string &shorterString(string &s1, string &s2)
{
    auto &r = shorterString(const_cast<const string&>(s1),
                            const_cast<const string&>(s2));
    return const_cast<string&>(r);
}

這裡使用兩次const_cast轉換,使用第一個shorterString函式定義了一個非常量版本的shorterString。

呼叫過載的函式

過載函式呼叫在編譯的時候會有函式匹配(function matching)的一個過程,在這個過程中把特定的函式呼叫與一組過載函式中的某一個關聯起來,函式匹配也叫做過載確定(overload resolution)。編譯器會將呼叫的實參和過載函式集合中每一個函式的形參作比較,然後根據比較的結果決定到底呼叫哪個函式。

過載函式有三種可能的結果:
- 編譯器找到一個與實參最佳匹配的函式,並生成呼叫該函式的程式碼。
- 找不到任何一個函式與呼叫的實參匹配,編譯器發出無匹配的錯誤資訊。
- 有多於一個函式可以匹配,但是每一個都不是明顯的最佳選擇,此時也將發生錯誤,成為二義性呼叫。

過載和作用域

我們知道,在同一個作用域中,內層作用域的宣告會覆蓋外層宣告,對於過載也是一樣的。如果我們在內層作用域中宣告名字,它將隱藏外層作用域中宣告個同名實體。在不同的作用域中無法過載函式名:

string read();
void print(const string &);
void print(double);
void fooBar(int ival)
{
    bool read = false; // 新作用域:隱藏了外層的read
    string s = read(); // 錯誤:read是一個布林值,而非函式
    // 不好的習慣:通常來說,在區域性作用域中宣告函式不是一個好的選擇
    void print(int);   // 新作用域:隱藏了之前的print
    print("value");    // 錯誤:print(const string&)被隱藏掉了
    print(ival);       // 正確:當前print可見
    print(3.14);       // 正確:呼叫print(int); print(double)被隱藏掉了
}

呼叫print函式時,編譯器首先尋找該函式名的宣告,找到的是print(int)這個區域性宣告,一旦在當前作用域中找到了所需的名字,編譯器就會忽略掉外層作用域的同名實體,所以print(“value”)這個呼叫是錯誤的。呼叫print(3.14)也是同樣的,double型轉換成int型,打印出整數值3。

我們把print(int)的宣告放在呼叫函式的外部,print(int)就成為了過載函式:

void print(const string &);
void print(double);
void print(int);
void fooBar(int ival)
{
    print("value");    // 呼叫 print(const string&)
    print(ival);       // 呼叫 print(int)
    print(3.14);       // 呼叫 print(double)

覆蓋

覆蓋(override)又稱為重寫,是指派生類中存在重新定義的函式,其函式名,引數列表,返回值型別,和基類中被重寫的函式一致。只有函式體不同(花括號內),派生類呼叫時會呼叫派生類的重寫函式,不會呼叫被重寫函式。基類中被重寫的函式必須有virtual修飾。

#include <iostream>

class Base {
public:
    virtual void foo() { std::cout << "Base class" << std::endl; }
};

class Derived : public Base {
public:
    void foo() override { std::cout << "Derived class" << std::endl; }
};

int main() {
    Base b;
    b.foo();      // ①
    Derived d;
    d.foo();      // ②
    Base &b1 = d;
    d.foo();      // ③
    Base *pb = &d;
    pb->foo();    // ④
    pb = &b;
    pb->foo();    // ⑤
}

輸出:

Base classDerived classDerived classDerived classBase class

派生類中foo函式後面的override說明該函式重寫了基類中的相同函式。示例程式中同時簡單演示了虛擬函式實現的多型。

隱藏

這個沒什麼好說的。有人說是派生類中定義了和基類名字相同的函式,就把派生類中的函式隱藏了,不考慮形參型別、形參個數和返回值。這個……我表示無法理解。

#include <iostream>

class Base {
public:
    void foo() { std::cout << "Base class" << std::endl; }
    int sum(int a, int b) { foo(); return a + b; }
};

class Derived : public Base {
public:
    void foo() { std::cout << "Derived class" << std::endl; }
    int sum(int b, int a) { foo(); return 1 + b + a;}
};

int main() {
    Base b;
    std::cout << b.sum(1, 2) << std::endl;
    Derived d;
    std::cout << d.sum(1, 2) << std::endl;
    Base &b1 = d;
    std::cout << b1.sum(1, 2) << std::endl;  // ③
    Base *pb = &d;
    std::cout << pb->sum(1, 2) << std::endl; // ④
    pb = &b;
    std::cout << pb->sum(1, 2) << std::endl;
}

上面這段程式碼的執行結果如下:

Base class
3
Derived class
4
Base class
3
Base class
3
Base class
3

從執行的結果來看,因為是普通成員函式,③和④兩處語句的輸出和虛擬函式表現的多型性輸出是不一樣的,這裡沒有表現出隱藏,把派生類sum函式的形參改成1個也不改變函式的匹配結果。

C++中沒有隱藏的相關規則,不知道為什麼把隱藏和過載和覆蓋放在一起說,以訛傳訛吧。

過載和覆蓋的區別

簡單總結一下兩者的區別:

  • (1)作用域區別:重寫和被重寫的函式在具有繼承關係的類中,過載和被過載的函式在同一作用域中。

  • (2)引數區別:重寫與被重寫的函式引數列表一定相同,過載和被過載的函式引數列表一定不同。

  • (3)virtual的區別:重寫的基類必須要有virtual修飾,過載函式和被過載函式可以被virtual修飾,也可以沒有。

相關推薦

C++過載覆蓋還有(隱藏)

前言 過載(overload)和覆蓋(override)是C++中關於函式的兩個基礎概念,但是如果讓你說出他們具體的描述和區別,一下子還真是不太容易說的很清楚和全面,這裡簡單把記錄一下,作為備忘。關於隱藏我覺得是個誤解,C++中根本沒有隱藏的說法和規則。

混跡於C++ 之過載覆蓋還有隱藏

摘自《高質量程式設計指南》林銳 過載與覆蓋 成員函式被過載的特徵是: -具有相同的作用域(即同一個類定義中)--如果位於不同的作用域,則為隱藏。 -函式名字相同。 -引數型別/順序或數目不同(包括const引數和非const引數)。 -virtual關鍵字可有可無。 覆蓋是

C++過載覆蓋隱藏的區別以及適用場景

一、過載、覆蓋和隱藏的區別 二、適用場景 1、過載:   適用於不同的資料型別都需要使用到的功能函式。以資料相加的函式為例,可以在同一個檔案內提供以下的過載函式以支援同樣的功能:   int add(int, int);/*2個整數相加*/   int add(int, int, int);/*3個整數相

C++過載覆蓋隱藏的區別

一、過載: 是函式名相同,引數列表不同 過載只是在類的內部存在。但是不能靠返回值型別來判斷。規則如下:1、相同的範圍(在同一個類中)2、函式名字相同3、引數不同4、Virtual關鍵字可有可無、二、覆蓋: 在繼承關係中,子類中定義了與父類同名的虛擬函式,從而子類自己本身定義的

Java過載覆蓋的異同點

方法的覆蓋和過載具有以下相同點: 都要求方法同名  都可以用於抽象方法和非抽象方法之間 方法的覆蓋和過載具有以下不同點: 方法覆蓋要求引數列表(引數簽名)必須一致,而方法過載要求引數列表必須不一致。  方法覆蓋要求返回型別必須一致,方法過載對此沒有要求。 

java過載覆蓋(又稱重寫)的區別

初次見到這兩個單詞並沒有什麼特別的感覺,但是時間長了,卻發現書上一會兒用override,一會兒又用overload,搞得我的迷迷糊。於是就做了個總結,希望能對和我一樣對這兩個概念模糊不清的網友有一個幫助。 override 可以翻譯為覆蓋,從字面就可以知道,它是覆蓋了一個

類成員函式過載/重寫(覆蓋)/重定義(隱藏)的區別

先來說說過載的含義,在日常生活中我們經常要清洗一些東西,比如洗車、洗衣服。儘管我們說話的時候並沒有明確地說用洗車的方式來洗車,或者用洗衣服的方式來洗一件衣服,但是誰也不會用洗衣服的方式來洗一輛車,否則等洗完時車早就散架了。我們並不要那麼明確地指出來就心知肚明,這就有過載的意思了。在同一可訪問區內被聲名的幾個具

C++過載、重寫(覆蓋隱藏的區別例項分析

本文例項講述了C++中過載、重寫(覆蓋)和隱藏的區別,對於C++面向物件程式設計來說是非常重要的概念。具體分析如下: 1.過載:過載從overload翻譯過來,是指同一可訪問區內被宣告的幾個具有不同引數列(引數的型別,個數,順序不同)的同名函式,根據引數列表確定呼叫哪個函式,過載不關心函式返回型

C++過載、重寫(覆蓋隱藏的區別

基本概念: 過載:是指同一可訪問區內被宣告的幾個具有不同引數列(引數的型別,個數,順序不同)的同名函式,根據引數列表確定呼叫哪個函式,過載不關心函式返回型別。 示例: class A{ public: void test(int i); voi

C++過載覆蓋隱匿函式多型

C++的多型特性是這門語言很重要的一個特性。 一、靜態多型:編譯器在編譯期間完成的,編譯器根據函式實參的型別(可能會進行隱式型別轉換),可推 斷出要呼叫那個函式,如果有對應的函式就呼叫該函式,否則出現編譯錯誤。 二、動態多型:在程式執行期間(非編譯期

C#&&&|||區別

.com blank img cnblogs png ref bsp 筆記 區別 當兩者都為邏輯運算符時。 其實沒什麽差別。 &&和||當已經確定結果時,不會對第二個操作數求值。也不知道什麽情況會用到這個差別。做個筆記好了。 http://blog.cs

我的女朋友漏電了–論C++的失敗(failure)缺陷(bug)異常(exception)

c++先做個廣告置入,如果喜歡這篇文章,你可以到 zhaoyan.website/blog 去查看於此類似的C/C++文章。我承認有點標題黨了,不過這真的是一篇寫軟件的文章,所以如果你已經抽出了一張面巾紙,那麽趁早再把它完美的放回去。這篇軟件文章很軟,源代碼不多,而且大部分都是偽代碼。所以很適合所有人看。我特

C++指標引用還有*&的關係

*是取值,&是取地址。 在函式定義宣告的時候按照谷歌規範,輸入是const &型別的,輸出是指標型別的。 在使用過程中如果輸入是上一個函式的輸出,在使用的時候需要使用&或者星號×指標進行處理。 在函式宣告過程中使用&表示引用,函式內對引數進行了修改外部也會發生

(1) C++:過載覆蓋隱藏

  C++之中的過載、覆蓋、隱藏   過載 覆蓋 過載與覆蓋的區別 相關程式碼 隱藏   過載 過載是指函式不同的引數表,對同名函式的名稱做修飾,然後這些同名函式就成了不同的函式。在同一可訪問區域內被宣告的幾

c++ string const char * 的相同不同遇到的坑。

#include<iostream> #include<string> using namespace std; string ToString(int i) { string str = std::to_string(i); return str; } int mai

過載覆蓋的區別通過反射獲取泛型實際型別列舉要點五個最常用的集合類之間的區別聯絡final總結

java面試碰到過的題目之方法過載和覆蓋的區別。 1. 過載方法必須滿足以下條件:  i. 方法名相同。 ii. 方法的引數型別、個數、順序至少有一項不同。 iii. 方法的返回型別可以不相同。 iv. 方法的修飾符可以不相同。 2. 重寫方法必須滿足以下條件:  i. 子

C++過載、重寫(覆蓋)的區別例項分析

本文例項講述了C++中過載、重寫(覆蓋)和隱藏的區別,對於C++面向物件程式設計來說是非常重要的

C++重寫newdelete比想像困難

  關於C++記憶體管理這話題,永遠都不過時。在我剛出道的時候,就已經在考慮怎麼檢測記憶體洩漏(https://www.cnblogs.com/coding-my-life/p/3985164.html)。想用一份簡單的程式碼,並且不太影響執行效率去實現記憶體洩漏檢測,是不太現實的。當時覺得重寫new和del

c++的訪問屬性繼承方式一些問題的總結

第一:private, public, protected 訪問標號的訪問範圍。 private:只能由1.該類中的函式、2.其友元函式訪問。 不能被任何其他訪問,該類的物件也不能訪問。 prot

c# 繫結combox同時獲取valuestext值

public void BindCombox(ComboBox cb, string id, string text,string str) //id 為value,text為text,str為得到資料集的語句 { DataTable dt = new DataTable()