C 函式過載和引數的預設值

2022-02-20 14:09:01 字數 3307 閱讀 3801

自然語言中,乙個詞可以有許多不同的含義,即該詞被過載了。人們可以通過上下文來判斷該詞到底是哪種含義。「詞的過載」可以使語言更加簡練。例如「吃飯」的含義十分廣泛,人們沒有必要每次非得說清楚具體吃什麼不可。別迂腐得象孔已己,說茴香豆的茴字有四種寫法。

在 c++程式中,可以將語義、功能相似的幾個函式用同乙個名字表示,即函式過載。這樣便於記憶,提高了函式的易用性,這是 c++語言採用過載機制的乙個理由。例如下例中的函式 eatbeef,eatfish,eatchicken 可以用同乙個函式名 eat 表示,用不同型別的引數加以區別。

// 過載函式 eat

void eatbeef(…); // 可以改為 void eat(beef …);

void eatfish(…); // 可以改為 void eat(fish …);

void eatchicken(…); // 可以改為 void eat(chicken …);

c++語言採用過載機制的另乙個理由是:類的建構函式需要過載機制。因為 c++規定建構函式與類同名,建構函式只能有乙個名字。如果想用幾種不同的方法建立物件該怎麼辦?別無選擇,只能用過載機制來實現。所以類可以有多個同名的建構函式。

幾個同名的過載函式仍然是不同的函式,它們是如何區分的呢?

我們自然想到函式介面的兩個要素:引數與返回值。 如果同名函式的引數不同(包括型別、順序不同),那麼容易區別出它們是不同的函式。

如果同名函式僅僅是返回值型別不同,有時可以區分,有時卻不能。例如:

void function(void);

int function (void);

上述兩個函式,第乙個沒有返回值,第二個的返回值是 int 型別。如果這樣呼叫

函式:

int x = function ();
則可以判斷出 function 是第二個函式。問題是在 c++/c 程式中,我們可以忽略函式的返回值。在這種情況下,編譯器和程式設計師都不知道哪個 function 函式被呼叫。

所以只能靠引數而不能靠返回值型別的不同來區分過載函式。編譯器根據引數為每個過載函式產生不同的內部識別符號。例如編譯器為上例中的三個 eat 函式產生像 _eat_beef、_eat_fish、_eat_chicken 之類的內部識別符號(不同的編譯器可能產生不同風格的內部識別符號)。

如果 c++程式要呼叫已經被編譯後的 c 函式,該怎麼辦?

假設某個 c 函式的宣告如下:

void foo(int x, int y);
該函式被 c 編譯器編譯後在庫中的名字為_foo,而 c++編譯器則會產生像 _foo_int_int 之類的名字用來支援函式過載和型別安全連線。由於編譯後的名字不同,c++程式不能直接呼叫 c 函式。c++提供了乙個 c 連線交換指定符號 extern「c」 來解決這個問題。例如:

extern 「c」

或者寫成:

extern 「c」

這就告訴 c++ 編譯譯器,函式 foo 是個 c 連線,應該到庫中找名字_foo 而不是找_foo_int_int。c++編譯器開發商已經對 c 標準庫的標頭檔案作了 extern「c」 處理,所以我們可以用 #include 直接引用這些標頭檔案。

注意並不是兩個函式的名字相同就能構成過載。全域性函式和類的成員函式同名不算過載,因為函式的作用域不同。例如:

void print(…); // 全域性函式

class a

不論兩個 print 函式的引數是否不同,如果類的某個成員函式要呼叫全域性函式 print,為了與成員函式 print 區別,全域性函式被呼叫時應加 『::』 標誌。例如:

::print(…); // 表示 print 是全域性函式而非成員函式

下例中,第乙個 output 函式的引數是 int 型別,第二個 output 函式的引數是 float 型別。由於數字本身沒有型別,將 數字當作引數時將自動進行型別轉換(稱為隱式型別轉換)。語句 output(0.5)將產生編譯錯誤,因為編譯器不知道該將 0.5 轉換成 int 還是 float 型別的引數。隱式型別轉換在很多地方可以簡化程式的書寫,但是也可能留下隱患。

# include void output( int x)

void output( float x)

void main(void)

有一些引數的值在每次函式呼叫時都相同,書寫這樣的語句會使人厭煩。c++ 語言採用引數的預設值使書寫變得簡潔(在編譯時,預設值由編譯器自動插入)。

引數預設值的使用規則:

【規則 2-1】引數預設值只能出現在函式的宣告中,而不能出現在定義體中。

void foo(int x=0, int y=0); // 正確,預設值出現在函式的宣告中

void foo(int x=0, int y=0) // 錯誤,預設值出現在函式的定義體中

為什麼會這樣?我想是有兩個原因:一是函式的實現(定義)本來就與引數是否有預設值無關,所以沒有必要讓預設值出現在函式的定義體中。二是引數的預設值可能會改動,顯然修改函式的宣告比修改函式的定義要方便。

【規則 2-2】如果函式有多個引數,引數只能從後向前挨個兒預設,否則將導致函式呼叫語句怪模怪樣。

void foo(int x, int y=0, int z=0); // 正確

void foo(int x=0, int y, int z=0); // 錯誤

要注意,使用引數的預設值並沒有賦予函式新的功能,僅僅是使書寫變得簡潔一些。它可能會提高函式的易用性,但是也可能會降低函式的可理解性。所以我們只能適當地使用引數的預設值,要防止使用不當產生負面效果。

#include void output( int x);

void output( int x, float y=0.0); // 引數預設值只能出現在函式的宣告中

void main(void)

void output( int x)

void output( int x, float y)

上例中,不合理地使用引數的預設值將導致過載函式 output 產生二義性。

參考:

《高質量c++c 程式設計指南 林銳》的第8章 c++函式的高階特性

C 函式引數預設值

函式引數在有預設值時,如果呼叫時沒有傳參,則預設push預設值。例項一 不帶預設值 include using namespace std intfun int a,int b,int c int main intfun int a,int b,int c 例項二 函式宣告與定義衝突 函式的預設值在...

C 函式的預設值 函式過載

一 函式的預設值 1 預設值一般寫在宣告中 可以寫多個宣告 int func int a,int b,int c 20 func 10,20 和 func 10,20,30 都可以 2 自左向右依次賦值 實參會替代形參 int func int a 10,int b,int c 20 func 10...

C 函式引數的預設值

我們可以賦予函式引數預設值。所謂預設值就是在呼叫時,可以不寫某些引數的值,編譯器會自動把預設值傳遞給呼叫語句中。關於預設值要注意幾點 1.我們通常是將預設值的設定放在宣告中而不是定義中。2.不能將實際值傳遞給引用型別的引數。可以將變數作引用型別引數的預設值,這時變數必須是已經宣告且是全域性變數。宣告...