C++深入淺出講解函數(shù)重載
前言
自然語(yǔ)言中,一個(gè)詞可以有多重含義,人們可以通過(guò)上下文來(lái)判斷該詞真實(shí)的含義,即該詞被重載了。
比如:以前有一個(gè)笑話,國(guó)有兩個(gè)體育項(xiàng)目大家根本不用看,也不用擔(dān)心。一個(gè)是乒乓球,一個(gè)是男足。前者是“誰(shuí)也贏不了!”,后者是“誰(shuí)也贏不了!”
函數(shù)重載
1.1 函數(shù)重載的概念
函數(shù)重載:
- 它是函數(shù)的一種特殊情況,C++允許在同一作用域中同一作用域中聲明幾個(gè)功能類(lèi)似的同名函數(shù)
- 函數(shù)重載的關(guān)鍵是函數(shù)的參數(shù)列表,也稱(chēng)為“函數(shù)特征標(biāo)”
- 這些同名函數(shù)的形參列表(參數(shù)個(gè)數(shù)、類(lèi)型和順序(不同類(lèi)型的順序))必須不同,常用來(lái)處理實(shí)現(xiàn)功能類(lèi)似數(shù)據(jù)類(lèi)型不同的問(wèn)題
- 函數(shù)重載也是多態(tài)的一種,多態(tài)指的是“有多種形式”
//C語(yǔ)言不支持重載,C++支持重載 int Add(int left, int right) { return left+right; } double Add(double left, double right) { return left+right; } int Add(int left, double right) { return left+right; } int Add(double left, int right) { return left+right; } int main() { Add(10, 20); Add(10.0, 20.0); Add(10, 20.0); Add(10.0, 20.0) return 0; }
下面兩個(gè)函數(shù)屬于函數(shù)重載嗎?
short Add(short left, short right) { return left+right; } int Add(short left, short right) { return left+right; } int main() { Add(10, 20); Add(10, 20); return 0; }
代碼解析:
- 上述代碼中的兩個(gè)函數(shù)不屬于函數(shù)重載
- 因?yàn)橹剌d的形參列表(參數(shù)個(gè)數(shù)、類(lèi)型和順序)必須不同
- 函數(shù)重載與函數(shù)返回值的類(lèi)型無(wú)關(guān),并且在函數(shù)調(diào)用時(shí),也是無(wú)法識(shí)別它的
1.2 函數(shù)重載的意義
意義:
在C語(yǔ)言中,想要定義多個(gè)不同類(lèi)型交換數(shù)據(jù)的子函數(shù),需要不同的函數(shù)名來(lái)命名,比如SweapA、SweapB…等等
void SweapA(int *pa, int *pb) { int temp = *pa; *pa = *pb; *pb = temp; } void SweapB(double *pa, double *pb) { double temp = *pa; *pa = *pb; *pb = temp; } int main() { int a = 10, b = 20; double c = 10.0, d = 20.0; SweapA(&a, &b); SweapB(&c), &d); return 0; }
- 但是,在C++中,通過(guò)函數(shù)重載,只需要命名一次就可以了
- 雖然跟C語(yǔ)言一樣要重復(fù)定義函數(shù),但是后面會(huì)學(xué)到函數(shù)模板后,可以很好的解決這個(gè)重復(fù)定義問(wèn)題
void Sweap(int *pa, int *pb) { int temp = *pa; *pa = *pb; *pb = temp; } void Sweap(double *pa, double *pb) { double temp = *pa; *pa = *pb; *pb = temp; } int main() { int a = 10, b = 20; double c = 10.0, d = 20.0; Sweap(&a, &b); Sweap(&c), &d); return 0; }
1.3 名字修飾(name Mangling)
名字修飾(name Mangling):
- C++為了跟蹤每一個(gè)重載函數(shù),它都會(huì)給這些函數(shù)指定一個(gè)私密身份
- 使用C++編譯器編寫(xiě)函數(shù)重載程序時(shí),C++編譯器將執(zhí)行一些奇特的操作 — — ---名稱(chēng)修飾 或 名稱(chēng)矯正
- 它根據(jù)函數(shù)原型中指定的形參對(duì)每個(gè)函數(shù)名進(jìn)行加密
- 對(duì)參數(shù)數(shù)目和類(lèi)型進(jìn)行編碼,添加的一組符號(hào)符合隨函數(shù)形參列表而異,修飾時(shí)使用的約定(函數(shù)名)隨編譯器而異
為什么C++支持重載,而C語(yǔ)言不支持呢?
- 在C/C++中,一個(gè)程序要運(yùn)行起來(lái),需要經(jīng)歷以下幾個(gè)階段:預(yù)處理、編譯、匯編、鏈接
- 預(yù)處理(.i):文件展開(kāi)、宏替換、條件編譯、去注釋
- 編譯(.s):檢查語(yǔ)法是否正確,生成匯編代碼
- 匯編(.o):將匯編代碼轉(zhuǎn)化成二進(jìn)制的機(jī)器碼
- 鏈接(a.out):生成符號(hào)表,找調(diào)用函數(shù)的地址,鏈接匹配,合并到一起
- 實(shí)際我們的項(xiàng)目通常是由多個(gè)頭文件和多個(gè)源文件構(gòu)成,當(dāng)前a.cpp中調(diào)用了b.cpp中定義的Add函數(shù)
- 在編譯后鏈接前的處理階段,a.o的目標(biāo)文件中沒(méi)有Add的函數(shù)地址,因?yàn)锳dd是在b.cpp中定義的,所以Add的地址在b.o中。那么怎么辦呢?
- 鏈接器看到a.o調(diào)用Add,但是沒(méi)有Add的地址,就會(huì)到b.o的符號(hào)表中找Add的地址,然后鏈接到一起
- 鏈接時(shí),面對(duì)Add函數(shù),鏈接器會(huì)使用哪個(gè)名字去找呢?這里每個(gè)編譯器都有自己的函數(shù)名修飾規(guī)則
在Linux下使用gcc和g++編譯器演示函數(shù)名被修飾后的名字
采用C語(yǔ)言編譯器編譯后結(jié)果(反匯編)
結(jié)論:在Linux下,采用gcc編譯完成后,函數(shù)名字的修飾沒(méi)有發(fā)生改變
采用C++編譯器編譯后結(jié)果(反匯編)
結(jié)論:在Linux下,采用g++編譯完成后,函數(shù)名字的修飾發(fā)生改變,編譯器將函數(shù)參數(shù)類(lèi)型信息添加到修改后的名字中
總結(jié)
gcc的函數(shù)修飾后名字不變。而g++的函數(shù)修飾后變成(_Z+函數(shù)長(zhǎng)度+函數(shù)名+類(lèi)型首字母)
C語(yǔ)言沒(méi)辦法支持重載,因?yàn)橥瘮?shù)沒(méi)辦法區(qū)分。而C++是通過(guò)函數(shù)修飾規(guī)則來(lái)區(qū)分,只要參數(shù)不同,修飾出來(lái)的名字就不一樣,就支持了重載
Windows下名字修飾規(guī)則
結(jié)論:對(duì)比Linux會(huì)發(fā)現(xiàn),windows下C++編譯器對(duì)函數(shù)名字修飾非常奇怪,但道理都是一樣的
擴(kuò)展學(xué)習(xí):C/C++函數(shù)調(diào)用約定和名字修飾規(guī)則
接下來(lái),再演示一個(gè)例子
f.h
#include <stdio.h>void f(int a, double b);
void f(double b, int a);f.cpp
#include "f.h"void f(int a, double b);
{
printf("%d %lf\n", a, b)
}void f(double b, int a);
{
printf("%lf %d\n", b, a)
}
Test.cpp
#include "f.h"int main()
{
f(1, 2.222);
f(2.222, 1);
return 0;
}
編譯后,生成匯編指令;鏈接時(shí),生成符號(hào)表
Linux下g++(C++)編譯器的命名:
Linux下gcc(C)編譯器的命名:
1.4 extern "C"
- 有時(shí)候在C++工程中可能需要將某些函數(shù)按照C的風(fēng)格來(lái)編譯
- 但是,大多數(shù)情況下是C工程需要將某些函數(shù)按照C++的風(fēng)格來(lái)編譯
- C可以調(diào)用CPP的靜態(tài)/動(dòng)態(tài)庫(kù),而CPP也可以調(diào)用C的靜態(tài)/動(dòng)態(tài)庫(kù)
- extern “C”是告訴編譯器,它所聲明的函數(shù),是C的庫(kù),要用C的鏈接方式去調(diào)用靜態(tài)庫(kù)或動(dòng)態(tài)庫(kù)
那么CPP是怎么調(diào)用C中的靜態(tài)/動(dòng)態(tài)庫(kù)呢?(vs2022演示)
首先,我們用C來(lái)生成一個(gè)靜態(tài)庫(kù)或動(dòng)態(tài)庫(kù)
Test.h #include <stdio.h> void PrintArray(int* p, int n); //顯示數(shù)組內(nèi)容 void InsertSort(int* p, int n); //插入排序 Test.C #include "Test.h" void InsertSort(int* p, int n) { for (int i = 0; i < n - 1; ++i) { int end = i; int tmp = p[end + 1]; while (end >= 0) { if (tmp < p[end]) { p[end + 1] = p[end]; --end; } else { break; } } p[end + 1] = tmp; } }
配置類(lèi)型改成靜態(tài)庫(kù)后,生成解決方案,就得到后綴.lib文件了
在CPP項(xiàng)目中添加新的庫(kù)目錄(這個(gè)庫(kù)是你生成的靜態(tài)庫(kù)的路徑)
增加新的依賴(lài)項(xiàng)(依賴(lài)項(xiàng)為生成靜態(tài)庫(kù)的文件名+后綴"Test.lib")
做完這些準(zhǔn)備后,我們來(lái)進(jìn)行編譯程序
- 編譯后我們發(fā)現(xiàn)鏈接階段時(shí)出現(xiàn)了錯(cuò)誤
- 原因是:C++調(diào)用C時(shí),它們之間的函數(shù)命名規(guī)則(名稱(chēng)修飾)不同
- 我們需要C++中的extern "C"來(lái)解決
- extern “C”是告訴編譯器,它所聲明的函數(shù),是C的庫(kù),要用C的鏈接方式去調(diào)用靜態(tài)庫(kù)或動(dòng)態(tài)庫(kù)
extern "C" { //"../"是在當(dāng)前目錄的上一個(gè)目錄中找文件 #include "../../Test/Test/Test.h" } #include <iostream> using namespace std; void TestInsertSort() { int Array[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; InsertSort(Array, sizeof(Array) / sizeof(Array[0])); for (int i = 0; i < 10; ++i) cout << Array[i] << " "; cout << " " << endl; } int main() { TestInsertSort(); return 0; }
如果C想調(diào)用CPP的靜態(tài)或動(dòng)態(tài)庫(kù)呢?
- C調(diào)用CPP庫(kù)時(shí),也會(huì)遇到名稱(chēng)修飾的問(wèn)題
- 這里需要對(duì)CPP的名稱(chēng)修飾規(guī)則改成C的規(guī)則
- 這里我們需要用到"條件編譯"來(lái)解決問(wèn)題
Test.h #include <stdio.h> #ifdef __cplusplus #define EXTERN_C extern "C" #else #define EXTERN_C #endif EXTERN_C void PrintArray(int* p, int n); EXTERN_C void InsertSort(int* p, int n); Test.cpp #include "Test.h" void PrintArray(int* p, int n) { for (int i = 0; i < n; ++i) { printf("%d ", p[i]); } printf("\n"); } void InsertSort(int* p, int n) { for (int i = 0; i < n - 1; ++i) { int end = i; int tmp = p[end + 1]; while (end >= 0) { if (tmp < p[end]) { p[end + 1] = p[end]; --end; } else { break; } } p[end + 1] = tmp; } }
感謝大家支持?。?!
到此這篇關(guān)于C++深入淺出講解函數(shù)重載的文章就介紹到這了,更多相關(guān)C++函數(shù)重載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言數(shù)據(jù)的存儲(chǔ)超詳細(xì)講解中篇練習(xí)
使用編程語(yǔ)言進(jìn)行編程時(shí),需要用到各種變量來(lái)存儲(chǔ)各種信息。變量保留的是它所存儲(chǔ)的值的內(nèi)存位置。這意味著,當(dāng)您創(chuàng)建一個(gè)變量時(shí),就會(huì)在內(nèi)存中保留一些空間。您可能需要存儲(chǔ)各種數(shù)據(jù)類(lèi)型的信息,操作系統(tǒng)會(huì)根據(jù)變量的數(shù)據(jù)類(lèi)型,來(lái)分配內(nèi)存和決定在保留內(nèi)存中存儲(chǔ)什么2022-04-04C語(yǔ)言實(shí)現(xiàn)按行讀寫(xiě)文件
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)按行讀寫(xiě)文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11vs code 配置c/c++環(huán)境的詳細(xì)教程(推薦)
這篇文章主要介紹了vs code 配置c/c++環(huán)境的詳細(xì)教程(推薦),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11