C++入門語法之函數重載詳解

寫在前面

關於C語言的編譯與鏈接不懂的可以看一下下面的文章,先回顧一下以前的知識。

詳解C語言的編譯與鏈接

1 函數重載的概念

函數重載:是函數的一種特殊情況,C++允許在同一作用域中聲明幾個功能類似的同名函數,這些同名函數的形參列表(參數個數 或 類型 或 順序)必須不同,常用來處理實現功能類似數據類型不同的問題。

//1.函數的參數個數不同
#include <iostream>
using namespace std;
void print()
{
	cout << "print()" << endl;
}
void print(int a)
{
	cout << "print(int a)" << endl;
}
int main()
{
	print();
	print(1);
	return 0;
}

執行結果如下:

print()
print(int a)
請按任意鍵繼續. . .

//2.函數的參數類型不同
#include <iostream>
using namespace std;
void print(double a)
{
	cout << "print(double a)" << endl;
}
void print(int a)
{
	cout << "print(int a)" << endl;
}
int main()
{
	print(1.1);
	print(1);
	return 0;
}

執行結果如下:

print(double a)
print(int a)
請按任意鍵繼續. . .

//3.函數的參數順序不同
#include <iostream>
using namespace std;
void print(double a, int b)
{
	cout << "print(double a, int b)" << endl;
}
void print(int b, double a)
{
	cout << "print(int b, double a)" << endl;
}
int main()
{
	print(1.1, 1);
	print(1, 1.1);
	return 0;
}

執行結果如下:

print(double a, int b)
print(int b, double a)
請按任意鍵繼續. . .

上面就是支持函數重載的三種情況,緊接著看如下兩個函數是否構成函數重載?

int Add(int num1, int num2)
{
	return num1 + num2;
}
double Add(int num1,int num2)
{
	return num1 + num2;
}
int main()
{
	return 0;
}

我們一編譯,編譯器就會報如下錯誤:

在這裡插入圖片描述

通過上面的分析,我們可以發現是否構成函數重載隻與這些同名函數的形參列表(參數個數 或 類型 或 順序)有關,與函數的返回值的類型無關。因此返回值不同,不能構成函數重載,在調用時無法區分。

下面思考如下兩個函數是否構成函數重載?

void print(int a)
{
	cout << "print()" << endl;
}
void print(int a = 0)
{
	cout << "print(int a = 0)" << endl;
}

我們一編譯,編譯器就會報如下錯誤:

在這裡插入圖片描述

因此函數參數缺省值不同,也不構成函數重載。

最後再看如下兩個函數是否構成函數重載?

void print(int a)
{
	cout << "print()" << endl;
}
void print(int a = 0)
{
	cout << "print(int a = 0)" << endl;
}

很顯然上面兩個函數是構成函數重載的,我們編譯也沒有任何問題,但是我們不傳參調用就會出問題,比如print(),就會在調用時出現歧義。

2 函數重載原理

通過上面的學習,我們現在對函數重載的語法有瞭一定的認識和理解,緊接著我們帶著如下問題來分析一下函數重載的原理。

為什麼C++支持函數重載,而C語言不支持函數重載呢?

首先我們在Linux底下創建三個文件,來驗證上面的問題,如下:

//func.h
#include <stdio.h>
void f();
void f(int a);
//func.c
void f()
{
	printf("f()\n");
}
void f(int a)
{
	printf("f(int a)\n");
}
//test.c
#include "func.h"
int main()
{
	f();
	f(1);
	return 0;
}

調用C的編譯器編譯test.c和func.c就會報如下錯誤:

在這裡插入圖片描述

因此,驗證瞭C語言不支持函數重載。因為編譯的時候 ,兩個重載函數,函數名相同,在func.o的符號表中存在歧義和沖突。其次,鏈接的時候也存在歧義和沖突,因為它們都是用函數名去標識和查找,而重載函數,函數名相同。

為瞭驗證上面的說法,我們屏蔽一個函數,調用C的編譯器編譯test.c和func.c,在linux底下會生成一個a.out的可執行程序,用objdump -S 來查看一下這個文件:

在這裡插入圖片描述

同理,我們把剛屏蔽的函數取消掉,由於C++是兼容C的,因此上面的程序我們可以用C++的編譯器去編譯,其結果如下

在這裡插入圖片描述

用objdump -S 來查看一下a.out:

在這裡插入圖片描述

這裡不難看出c++目標文件中的符號表中不是直接用函數名來標識和查找函數。而是引入瞭函數名修飾規則,不同編譯器下的函數名修飾規則不同。

g++的函數名修飾規則:_Z + 函數名長度 + 函數名 + 參數類型首字母。有瞭函數名修飾規則,隻要函數參數不同,在func.o符號表裡面重載的函數就不存在二義性和沖突瞭。

其次,鏈接的時候,test.o裡面的main函數去調用兩個重載函數時,去符號表裡面查找地址時也是明確的。

在這裡插入圖片描述

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容

推薦閱讀: