1. 程式人生 > >C++中的函式過載

C++中的函式過載

初識C++

C++ 是一種靜態型別的、編譯式的、通用的、大小寫敏感的、不規則的程式語言,支援過程化程式設計、面向物件程式設計和泛型程式設計。 C++ 被認為是一種中級語言,它綜合了高階語言和低階語言的特點。 C++ 是由 Bjarne Stroustrup 於 1979 年在新澤西州美利山貝爾實驗室開始設計開發的。C++ 進一步擴充和完善了 C 語言,最初命名為帶類的C,後來在 1983 年更名為 C++。 C++ 是 C 的一個超集,事實上,任何合法的 C 程式都是合法的 C++ 程式。

C++的函式過載

什麼是函式過載

函式過載是指C++允許在同一作用域中宣告幾個功能類似的同名函式,這些同名函式的形參列表(引數個數、型別、順序)必須不同,常用來處理實現功能類似資料型別不同的問題。

函式過載的作用

函式過載可以使我們為一類功能相近或相同的函式起一個統一的名字,這樣做可以使我們的函式呼叫更加統一,方便。

如何實現函式過載

下面是一個實現函式過載的例子

#include <iostream>

using namespace std;

int Add(int left, int right)
{
    return left+right;
}

double Add(double left, double right)
{
   return left+right;
}

long Add(long left, long right)
{
    return
left+right; } int main() { Add(10, 20); Add(10.0, 20.0); Add(10L, 20L); return 0; }

通過這個例子我們不難看出,在C++中,這些形成過載的函式擁有相同的函式名,但是他們擁有不同的引數列表(引數個數,引數型別,順序不同,僅函式返回值型別不同無法構成函式過載),構成過載的函式可以直接被呼叫,系統會根據傳入的實參自動選擇相匹配的函式進行呼叫。

為什麼C++可以實現函式過載

想要解答這個問題,就不得不提C++與C語言在函式名字修飾上的區別:

在C/C++中,一個程式要執行起來,需要經歷以下幾個階段:預處理、編譯、彙編、連結

Name Mangling是一種在編譯過程中,將函式、變數的名稱重新改編的機制,簡單來說就是編譯器為了區分各個 函式,將函式通過一定演算法,重新修飾為一個全域性唯一的名稱。

C語言的名字修飾規則非常簡單,只是在函式名字前面添加了下劃線。比如,對於以下程式碼,在最後連結時就會出錯:

int Add(int left, int right);
 
int main() {
	Add(1, 2);
	return 0; 
}

編譯器報錯:error LNK2019: 無法解析的外部符號 _Add,該符號在函式 _main 中被引用。

上述Add函式只給了宣告沒有給定義,因此在連結時就會報錯,從報錯結果中可以看到,C語言只是在函式名前添加了下劃線。因此當工程中存在相同函式名的函式,就會產生衝突。

而在C++中,函式命名規則並沒有這麼簡單,如以下程式碼

int Add(int left, int right);
double Add(double left, double right);

int main() {
    Add(1, 2);
    Add(1.0, 2.0);
    return 0;
}

error LNK2019: 無法解析的外部符號 “double cdecl Add(double,double)” ([email protected]@[email protected]) error LNK2019: 無法解析的外部符號 “int __cdecl Add(int,int)” ([email protected]@[email protected])

在這裡,我們並不深究C++語言對函式的命名規則,你可以先理解為在C++語言中,由於命名規則的不同,你所寫下的那些形成過載的同名函式,在編譯器看來是完全不同的函式,因此,編譯器會在你呼叫這些函式的時候根據你所傳入的引數,自動選擇相匹配的函式執行。

預設引數

什麼是預設引數

預設引數是宣告或定義函式時為函式的引數指定一個預設值。在呼叫該函式時,如果沒有指定實參則採用該預設值,否則使用指定的實參。

如何使用預設引數

下面我們通過一個例子來看一下如何使用預設引數

 void TestFunc(int a = 0)
{
    cout<<a<<endl;
}
int main() {
    TestFunc();		// 沒有傳參時,使用引數的預設值 
    TestFunc(10);	// 傳參時,使用指定的實參
}

從以上程式碼我們可以看出,在給定預設值後,函式的呼叫可以省去實參,函式會自動使用預設的數值

全部預設和部分預設

全部預設

   void TestFunc(int a = 10, int b = 20, int c = 30)
{
    cout<<"a = "<<a<<endl;
    cout<<"b = "<<b<<endl;
    cout<<"c = "<<c<<endl;
}

部分預設

 void TestFunc(int a, int b = 10, int c = 20)
{
    cout<<"a = "<<a<<endl;
    cout<<"b = "<<b<<endl;
    cout<<"c = "<<c<<endl;
}

注意

  1. 半預設引數必須從右往左依次來提供,不能間隔著給出
  2. 預設引數不能同時在函式宣告和定義中出現,當宣告與定義分離時,只能在宣告中出現

名稱空間

什麼是名稱空間

在C/C++中,變數、函式和後面要學到的類都是大量存在的,這些變數、函式和類的名稱將都存在於全域性作用域 中,可能會導致很多衝突。使用名稱空間的目的是對識別符號的名稱進行本地化,以避免命名衝突或名字汙染, namespace關鍵字的出現就是針對這種問題的

定義一個名稱空間

定義名稱空間,需要使用到namespace關鍵字,後面跟名稱空間的名字,然後接一對{}即可,{}中即為名稱空間的 成員。

  1. 普通的名稱空間
namespace N1 // N1為名稱空間的名稱 
{
// 名稱空間中的內容,既可以定義變數,也可以定義函式 
	int a;
	int Add(int left, int right)
	{
       return left + right;
   	}
}
  1. 名稱空間可以巢狀
namespace N2
{
   int a;
   int b;
   int Add(int left, int right)
   {
       return left + right;
   }
   namespace N3
   {
       int c;
       int d;
       int Sub(int left, int right)
       {
           return left - right;
       }
	} 
}

注意:一個名稱空間就定義了一個新的作用域,名稱空間中的所有內容都侷限於該名稱空間中

名稱空間的使用

名稱空間的使用有三種方式:

  1. 加名稱空間名稱及作用域限定符
int main() {
    printf("%d\n", N::a);
	return 0; 
}
  1. 使用using將名稱空間中成員引入
using N::b;
int main()
{
    printf("%d\n", N::a);
    printf("%d\n", b);
    return 0;
}
  1. 使用using namespace 名稱空間名稱引入
using namespce N;
int main()
{
    printf("%d\n", N::a);
    printf("%d\n", b);
    Add(10, 20);
    return 0;
}