美文网首页C++基础
函数重载、extern C、默认参数

函数重载、extern C、默认参数

作者: 叶子扬 | 来源:发表于2019-10-11 16:59 被阅读0次
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 */

相关文章

网友评论

    本文标题:函数重载、extern C、默认参数

    本文链接:https://www.haomeiwen.com/subject/dgkxmctx.html