美文网首页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