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

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 */
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345