C++.png
函數重載 Overload
Overload就是同一個上下文允許出現同名函數,但是參數個數不同、參數類型不同、參數順序不同。函數被調用的時候,就是依據這幾個差異區分調用哪個函數。
#include <iostream>
using namespace std;
int sum (int a, int b){
return a + b;
}
long sum (long a, long b){
return a + b;
}
int main(int argc, const char * argv[]) {
cout << sum(2, 3) << endl;
cout << sum(2L, 3L) << endl;
return 0;
}
查看匯編:程序會根據不同的參數類型,調用相應的方法
0x100001250 <+0>: pushq %rbp
0x100001251 <+1>: movq %rsp, %rbp
0x100001254 <+4>: subq $0x20, %rsp
0x100001258 <+8>: movl $0x0, -0x4(%rbp)
0x10000125f <+15>: movl %edi, -0x8(%rbp)
0x100001262 <+18>: movq %rsi, -0x10(%rbp)
-> 0x100001266 <+22>: movl $0x2, %edi
0x10000126b <+27>: movl $0x3, %esi
0x100001270 <+32>: callq 0x100001090 ; sum at main.cpp:12
...
...
0x100001292 <+66>: movl $0x2, %edi
0x100001297 <+71>: movl $0x3, %esi
0x10000129c <+76>: movq %rax, -0x18(%rbp)
0x1000012a0 <+80>: callq 0x1000010b0 ; sum at main.cpp:17
Overload與返回值類型無關(與返回值無關),也就是不能通過不過不同的返回值來區分函數。
另外,實參的隱式類型轉換,可能會產生二義性
例如以下重載就會產生二義性
#include <iostream>
using namespace std;
void dispaly(long a){
cout << "dispaly(long a)" << a <<endl;
}
void dispaly(double a){
cout << "dispaly(double a)" << a <<endl;
}
int main(int argc, const char * argv[]) {
dispaly(10);
return 0;
}
因為dispaly(10)
參數為int
類型,而重載函數參數為long
類型或者double
類型,實參隱式類型轉換時就有多種選擇而不能轉換
本質vs原理
C++采用了name mangling
或者叫name decoration
技術,
C++編譯器默認會對符號名(變量名、函數名等)進行改編、修飾,有些地方翻譯為命名傾軋/命名改編。
重載時會生成多個不同的函數名,不同編譯器(MSVC、g++)有不同的生成規則。
例如上面的兩個函數:
int sum (int a, int b);
long sum (long a, long b);
經過編譯后,生成的函數名可能是:
sum_i_i;
sim_l_l;
默認參數
C++支持函數設置默認,在調用的時候如果省略實參,編譯器就會傳入默認參數
#include <iostream>
using namespace std;
int sum (int a = 5, int b = 10){
return a + b;
}
int main(int argc, const char * argv[]) {
cout << sum(2, 3) << endl;
cout << sum(2) << endl;
cout << sum() << endl;
return 0;
}
//輸出:
5
12
15
查看匯編:
// 三次調用都是同一個函數,如果沒有傳參,編譯器會傳入默認參數
0x100001266 <+22>: movl $0x2, %edi
0x10000126b <+27>: movl $0x3, %esi
0x100001270 <+32>: callq 0x100001090 ; sum at main.cpp:12
...
...
0x100001292 <+66>: movl $0x2, %edi
0x100001297 <+71>: movl $0xa, %esi ;這里編譯器幫我們傳入了10這個參數
0x10000129c <+76>: movq %rax, -0x18(%rbp)
0x1000012a0 <+80>: callq 0x100001090 ; sum at main.cpp:12
...
...
-> 0x1000012c2 <+114>: movl $0x5, %edi
0x1000012c7 <+119>: movl $0xa, %esi
0x1000012cc <+124>: movq %rax, -0x20(%rbp)
0x1000012d0 <+128>: callq 0x100001090 ; sum at main.cpp:12
- 注意:
- 默認參數只能按照從右到左的順序
- 如果函數同時有聲明、實現,默認參數只能放在函數聲明中
// 函數重載、默認參數可能會產生沖突、二義性(建議優先選擇使用默認參數)
void display(long a, long b = 10){
cout << a + b << endl;
}
void display(double a){
cout << a << endl;
}
int main() {
display(10);
return 0;
}
// 其中,display(10);報錯信息:
Call to 'display' is ambiguous
有歧義
// - 默認參數的值可以是常量、全局符號(變量、函數名)
void haha(){
cout << "haha()" << endl;
}
void func(int a, void(*block)()){
a++;
block();
cout << a << endl;
}
//調用:
func(10, haha);
//也可以這樣調用:
void(*p)() = haha;
func(10, p);
//輸出都是一樣的:
haha()
11
extern "C"
被extern "C"
修飾的代碼,會按照C語言方式編譯
格式:
格式1:
extern "C" func1();
extern "C" func2();
格式2:
extern "C"{
func1();
func2();
}
使用場景:
由于C、C++
編譯規則的不同,在C、C++
混合開發時,C++
在調用C語言API時,需要使用extern "C"
修飾C語言的函數聲明。
例如,C寫的第三方庫
如下示例,C語言不認識extern C
,使用宏__cplusplus
區分C和C+ +
另外,為了避免重復include頭文件,使用 #ifndef __FILENAME_H
來做flag判斷
#pragma once
也是包含一次,但是作用域是整個文件,而#ifndef、#define、#endif
可以只針對某段代碼
#ifndef sum_h
#define sum_h
#ifdef __cplusplus
extern "C" {
#endif
int sum(int a, int b);
#ifdef __cplusplus
}
#endif
#endif /* sum_h */