C++超詳細(xì)分析函數(shù)重載的使用
一、函數(shù)重載分析(上)
1.1 重載的定義
定義:同一個(gè)標(biāo)識(shí)符在不同的上下文有不同的意義
1.2 函數(shù)重載的定義
- 用同一個(gè)函數(shù)名定義不同的函數(shù)
- 當(dāng)函數(shù)名和不同的參數(shù)搭配時(shí)函數(shù)的含義不同
如下:
下面看一段代碼,感受一下:
#include <stdio.h> #include <string.h> int func(int x) { return x; } int func(int a, int b) { return a + b; } int func(const char* s) { return strlen(s); } int main(int argc, char *argv[]) { printf("%d\n", func(3)); printf("%d\n", func(4, 5)); printf("%d\n", func("D.T.Software")); return 0; }
下面為輸出結(jié)果:
1.3 函數(shù)重載需要滿足的條件
函數(shù)重載至少滿足下面的一個(gè)條件:
- 參數(shù)個(gè)數(shù)不同
- 參數(shù)類型不同
- 參數(shù)順序不同
下圖所示就是參數(shù)的順序不同:
下面看一個(gè)函數(shù)默認(rèn)參數(shù)遇上函數(shù)重載的實(shí)例程序:
#include <stdio.h> int func(int a, int b, int c = 0) { return a * b * c; } int func(int a, int b) { return a + b; } int main(int argc, char *argv[]) { int c = func(1, 2); return 0; }
下面為輸出結(jié)果:
編譯報(bào)錯(cuò),因?yàn)槟@鈨煽伞H绻f(shuō)調(diào)用第一個(gè)函數(shù)說(shuō)的過(guò)去,因?yàn)榉虾瘮?shù)默認(rèn)參數(shù)規(guī)則,c 的值已經(jīng)確定;調(diào)用第二個(gè)函數(shù)也符合常理,所以編譯不會(huì)通過(guò)。
1.4 編譯器調(diào)用重載函數(shù)的準(zhǔn)則
將所有同名函數(shù)作為候選者
嘗試尋找可行的候選函數(shù)
- 精確匹配實(shí)參
- 通過(guò)默認(rèn)參數(shù)能夠匹配實(shí)參
- 通過(guò)默認(rèn)類型轉(zhuǎn)換匹配實(shí)參
匹配失敗
- 最終尋找到的候選函數(shù)不唯一,則出現(xiàn)二義性,編譯失敗。
- 無(wú)法匹配所有候選者,函數(shù)未定義,編譯失敗。
1.5 函數(shù)重載的注意事項(xiàng)
- 重載函數(shù)在本質(zhì)上是相互獨(dú)立的不同函數(shù)
- 重載函數(shù)的函數(shù)類型不同
- 函數(shù)返回值不能作為函數(shù)重載的依據(jù)
函數(shù)重載是由函數(shù)名和參數(shù)列表決定的!?。?/p>
函數(shù)重載的本質(zhì)是什么?下面通過(guò)一段代碼深入分析,編譯環(huán)境為VS2012。
#include "stdafx.h" #include <stdio.h> int add(int a, int b) // int(int, int) { return a + b; } int add(int a, int b, int c) // int(int, int, int) { return a + b + c; } int main() { printf("%p\n", (int(*)(int, int))add); printf("%p\n", (int(*)(int, int, int))add); return 0; }
由C語(yǔ)言的知識(shí)可以知道,函數(shù)名就是函數(shù)的入口地址,所以輸出結(jié)果如下:
可以看到,兩個(gè) add() 函數(shù)的入口地址不一樣,所以這兩個(gè) add 是兩個(gè)不同的函數(shù)。
編譯器是如何看待這兩個(gè) add() 函數(shù)的呢?下面來(lái)深入分析。先看一下編譯器產(chǎn)生的中間結(jié)果,在Test -> Debug -> Test.obj 文件中。
然后使用VS2012里面自帶的命令行工具查看 Test.obj 里面有什么東西。
上圖示為VS2012 命令行所在位置
輸入 dumpbin,如下:
這里只需要關(guān)系 SYMBOLS(符號(hào)表),符號(hào)表就是編譯器在編譯過(guò)程中根據(jù)源代碼所生成的一張表,這張表有程序的函數(shù)名變量等等。
輸入以下命令,其中 /symbols 后面為 Test.obj 所在的位置。
找到下面的地方,可以看到編譯器編譯 (int __cdecl add(int,int)) 時(shí)標(biāo)識(shí)符為?add@@YAHHH@Z;而編譯器編譯(int __cdecl add(int,int,int)) 時(shí)標(biāo)識(shí)符為?add@@YAHHHH@Z ,也就是說(shuō)編譯器在編譯這兩個(gè)函數(shù)時(shí)已經(jīng)把這兩個(gè)函數(shù)分別對(duì)待,盡管它們名字一樣,所以兩個(gè) add() 函數(shù)的入口地址不一樣,這就很好理解了。
1.6 小結(jié)
- 函數(shù)重載是 C++ 中引入的概念
- 函數(shù)重載用于模擬自然語(yǔ)言中的詞匯搭配
- 函數(shù)重載使得 C++ 具有更豐富的語(yǔ)義表達(dá)能力
- 函數(shù)重載的本質(zhì)為相互獨(dú)立的不同函數(shù)
- C++ 中通過(guò)函數(shù)名和函數(shù)參數(shù)確定函數(shù)調(diào)用
二、函數(shù)重載分析(下)
2.1 函數(shù)重載遇上函數(shù)指針
將重載函數(shù)名賦值給函數(shù)指針時(shí)
- 根據(jù)重載規(guī)則挑選與函數(shù)指針參數(shù)列表一致的候選者
- 嚴(yán)格匹配候選者的函數(shù)類型與函數(shù)指針的函數(shù)類型
下面看一段代碼:
#include <stdio.h> #include <string.h> int func(int x) { return x; } int func(int a, int b) { return a + b; } int func(const char* s) { return strlen(s); } typedef int(*PFUNC)(int a); int main(int argc, char *argv[]) { int c = 0; PFUNC p = func; c = p(1); printf("c = %d\n", c); return 0; }
下面為輸出結(jié)果:
這也就是前面說(shuō)的通過(guò)函數(shù)指針?biāo)赶虻暮瘮?shù)類型參數(shù)列表來(lái)進(jìn)行選擇。
注意事項(xiàng)
- 函數(shù)重載必然發(fā)生在同一個(gè)作用域中
- 編譯器需要用參數(shù)列表或函數(shù)類型進(jìn)行函數(shù)選擇
- 無(wú)法直接通過(guò)函數(shù)名得到重載函數(shù)的入口地址(可以通過(guò)指針來(lái)獲取)
如下,這段代碼想通過(guò)函數(shù)名獲取重載函數(shù)的入口地址:
#include <stdio.h> int add(int a, int b) // int(int, int) { return a + b; } int add(int a, int b, int c) // int(int, int, int) { return a + b + c; } int main() { printf("%p\n", add); printf("%p\n", add); return 0; }
編譯的時(shí)候會(huì)報(bào)錯(cuò),無(wú)法確定是哪個(gè)函數(shù)。
2.2 C++和C的相互調(diào)用
- 實(shí)際工程中C++和C代碼相互調(diào)用是不可避免的
- C++編譯器能夠兼容C語(yǔ)言的編譯方式
- C++編譯器會(huì)優(yōu)先使用C++編譯的方式
- extern 關(guān)鍵字能強(qiáng)制讓C++編譯器進(jìn)行C方式的編譯
如下:
在 Linux環(huán)境下新建一個(gè) 9-2 文件夾,先在文件夾下新建 add.c 和 add.h 文件,如下:
add.c :
#include "add.h" int add(int a, int b) { return a + b; }
add.h :
int add(int a, int b);
通過(guò) linux 命令 cd 進(jìn)入 9-2 文件夾,再將 add.c 轉(zhuǎn)換成 add.o 文件,如下所示:
然后在 9-2 文件夾下建一個(gè) main.cpp 文件,如下:
mian.cpp :
#include <stdio.h> #include "add.h" int main() { int c = add(1,2); printf("c = %d\n", c); return 0; }
對(duì)程序進(jìn)行編譯,發(fā)現(xiàn)程序報(bào)錯(cuò),沒(méi)有定義 add() 函數(shù),但是函數(shù)確實(shí)已經(jīng)定義了,可以使用 linux 中的 nm 指令查看 add.o 里面的信息,打印出來(lái)的就是符號(hào)表信息,可以看到確實(shí)有 add 。
這個(gè)時(shí)候就需要使用 extern關(guān)鍵字強(qiáng)制讓C++編譯器進(jìn)行C方式的編譯,所以 main.cpp就要修改成這樣:
#include <stdio.h> extern "C" { #include "add.h" } int main() { int c = add(1,2); printf("c = %d\n", c); return 0; }
這樣編譯就能通過(guò)了:
如果在 9-2 文件中新建一個(gè) main.c 文件,main.c 里面的代碼 與 main.cpp 中的相同。
進(jìn)行編譯,發(fā)現(xiàn)會(huì)報(bào)錯(cuò)誤,因?yàn)?extern 關(guān)鍵詞寫(xiě)法是 C++ 中的, C語(yǔ)言不支持該寫(xiě)法。那有沒(méi)有一種寫(xiě)法既能被 C語(yǔ)言編譯通過(guò),又能讓 C++編譯通過(guò)呢?且看下面。
2.3 使得C代碼只會(huì)以C的方式被編譯的解決方案
- _cplusplus 是C++編譯器內(nèi)置的標(biāo)準(zhǔn)宏定義
- _cplusplus 的意義是確保C代碼以統(tǒng)一的C方式被編譯成目標(biāo)文件
如下:
所以上述代碼可以寫(xiě)作,main.c和 main.cpp 均為:
#include <stdio.h> #ifdef __cplusplus extern "C" { #endif #include "add.h" #ifdef __cplusplus } #endif int main() { int c = add(1, 2); printf("c = %d\n", c); return 0; }
這樣程序在 C語(yǔ)言和 C++ 的編譯環(huán)境下均能通過(guò),如下:
注意事項(xiàng)
C++編譯器不能以C的方式編譯重載函數(shù)
編譯方式?jīng)Q定函數(shù)名被編譯后的目標(biāo)名
- C++編譯方式將函數(shù)名和參數(shù)列表編譯成目標(biāo)名
- C 編譯方式只將函數(shù)名作為目標(biāo)名進(jìn)行編譯
下面通過(guò)一個(gè)例子說(shuō)明一下:
int add(int a, int b) // int(int, int) { return a + b; } int add(int a, int b, int c) // int(int, int, int) { return a + b + c; }
將該代碼編譯成目標(biāo)文件,取名為 test.oo,然后通過(guò) linux 中的 nm 命令查看 test.oo 中的東西,可以看到 test 符號(hào)表里面有兩個(gè)東西 T _Z3addii 和T _Z3addiii,這就是 add 函數(shù)被編譯過(guò)后的目標(biāo)函數(shù)名,ii 表示兩個(gè)參數(shù), iii 表示三個(gè)參數(shù)。
如果采用 C 方式編譯重載函數(shù),代碼如下:
extern "C" { int add(int a, int b) // ==>add { return a + b; } int add(int a, int b, int c) // ==>add { return a + b + c; } }
下面為編譯結(jié)果,可以看到編譯報(bào)錯(cuò),說(shuō)兩個(gè) add() 函數(shù)沖突了。
2.4 小結(jié)
- 函數(shù)重載是 C++ 對(duì) C 的一個(gè)重要升級(jí)
- 函數(shù)重載通過(guò)函數(shù)參數(shù)列表區(qū)分不同的同名函數(shù)
- extern 關(guān)鍵字能夠?qū)崿F(xiàn) C 和 C++的相互調(diào)用
- 編譯方式?jīng)Q定符號(hào)表中的函數(shù)名的最終目標(biāo)名
到此這篇關(guān)于C++超詳細(xì)分析函數(shù)重載的使用的文章就介紹到這了,更多相關(guān)C++函數(shù)重載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中的多態(tài)問(wèn)題—理解虛函數(shù)表及多態(tài)實(shí)現(xiàn)原理
這篇文章主要介紹了C++中的多態(tài)問(wèn)題—理解虛函數(shù)表及多態(tài)實(shí)現(xiàn)原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02C++實(shí)現(xiàn)產(chǎn)生隨機(jī)數(shù)和相應(yīng)的猜拳小游戲?qū)嵗a
C++中沒(méi)有自帶的random函數(shù),要實(shí)現(xiàn)隨機(jī)數(shù)的生成就需要使用rand()和srand()。下面這篇文章主要給大家介紹了關(guān)于C++實(shí)現(xiàn)產(chǎn)生隨機(jī)數(shù)和相應(yīng)的猜拳小游戲的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-09-09C語(yǔ)言中的運(yùn)算符和結(jié)合性問(wèn)題
這篇文章主要介紹了C語(yǔ)言中的運(yùn)算符和結(jié)合性問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03適合初學(xué)者的C語(yǔ)言轉(zhuǎn)義字符講解
轉(zhuǎn)義字符是很多程序語(yǔ)言、數(shù)據(jù)格式和通信協(xié)議的形式文法的一部分。對(duì)于一個(gè)給定的字母表,一個(gè)轉(zhuǎn)義字符的目的是開(kāi)始一個(gè)字符序列,使得轉(zhuǎn)義字符開(kāi)頭的該字符序列具有不同于該字符序列單獨(dú)出現(xiàn)(沒(méi)有轉(zhuǎn)義字符開(kāi)頭)時(shí)的語(yǔ)義。因此轉(zhuǎn)義字符開(kāi)頭的字符序列被叫做轉(zhuǎn)義序列2022-04-04對(duì)C語(yǔ)言中sizeof細(xì)節(jié)的三點(diǎn)分析介紹
以下是對(duì)C語(yǔ)言中sizeof的細(xì)節(jié)進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以參考下2013-07-07