函数重载 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 */