【026】C++的内联函数、函数重载、函数的默认参数与占位参数

news2024/11/20 12:44:40

C++的内联函数、函数重载、函数的默认参数与占位参数

  • 引言
  • 一、内联函数
    • 1.1、声明内联函数
    • 1.2、宏函数和内联函数的区别
    • 1.3、内联函数的注意事项
  • 二、函数重载
    • 2.1、函数重载的概述
    • 2.2、函数重载的条件
    • 2.3、函数重载的底层实现原理
  • 三、函数的默认参数
  • 四、占位参数
  • 五、extern "C"浅析
  • 总结

引言


💡 作者简介:专注于C/C++高性能程序设计和开发,理论与代码实践结合,让世界没有难学的技术。包括C/C++、Linux、MySQL、Redis、TCP/IP、协程、网络编程等。
👉
🎖️ CSDN实力新星,社区专家博主
👉
🔔 专栏介绍:从零到c++精通的学习之路。内容包括C++基础编程、中级编程、高级编程;掌握各个知识点。
👉
🔔 专栏地址:C++从零开始到精通
👉
🔔 博客主页:https://blog.csdn.net/Long_xu


🔔 上一篇:【025】C++对C的扩展之引用(reference)详解

一、内联函数

C++中的内联函数是一种特殊类型的函数,它在编译时会被直接嵌入到调用它的代码中,而不是像普通函数那样进行函数调用。这种方式可以减少函数调用的开销,提高程序的运行效率。

以下是定义内联函数的语法:

inline 返回类型 函数名(参数列表) {
  // 函数体
}

其中,关键字“inline”表示这个函数是一个内联函数。需要注意的是,只有短小精悍的代码才适合使用内联函数。如果内联函数过于复杂,则可能会导致代码膨胀和可读性下降。

另外,在使用类成员函数时,默认情况下编译器会将其视为内联函数。因此,在类定义中声明和实现成员函数时不需要加上“inline”关键字。

1.1、声明内联函数

内联函数必须在定义的时候使用关键字inline修饰,不能在声明的时候使用inline。

// 函数声明时不要使用inline关键字
int mAdd(int x,int y);
int main()
{
	cout<<mAdd(100,200)<<endl;
}
// 内联函数在定义的时候使用inline
inline mAdd(int x,int y)
{
	return x+y;
}

内联函数 在编译阶段 将内联函数中的函数体替换函数调用处,避免函数调用时的开销。

1.2、宏函数和内联函数的区别

宏函数和内联函数都会在适当的位置进行展开,避免函数调用开销。

  • 宏函数在预处理阶段展开,内联函数在编译阶段展开。
  • 宏函数的参数没有类型,不能保证参数的完整性;内联函数有参数类型,能保证参数的完整性。
  • 宏函数没有作用域的限制,不能作为命名空间、结构体、类的成员;内联函数有作用域的限制,能作为命名空间、结构体、类的成员。

1.3、内联函数的注意事项

(1)在内联函数定义的时候加inline修饰。
(2)类中的成员函数默认 都是内联函数(不加inline也是内联函数)。
(3)有时候就算加上inline也不一定是内联函数,能不能成为内联函数有以下的内联函数条件决定:

  • 不能存在任何形式的循环语句;
  • 不能存在过多的条件判断语句;
  • 函数体不能过于庞大;
  • 不能对函数取地址。

(4)有时候不加inline修饰也有可能是内联函数。
(5)是不是内联函数 由编译器决定。

也就是说,我们添加inline修饰只是希望这个函数是内联函数,但是能不能成为内联函数由编译器决定。

二、函数重载

2.1、函数重载的概述

C++函数重载是指在一个类中定义多个同名函数,这些函数的参数列表不同(参数数量、类型或顺序等),以便根据调用时传递的实参来选择合适的函数进行调用。其实现原理是通过编译器对每个重载函数生成唯一的名称来区分不同的函数。

函数重载可以提高代码复用性和可读性,同时也方便了程序员使用和维护。需要注意的是,虽然多个函数名相同但参数不同的函数被视为不同的函数,但返回值类型不能作为区分标准。此外,如果两个或多个重载函数有相似功能,建议将它们设计成共享一个基础算法或操作,并且保持接口统一。

同一个函数名在不同场景下可以具有不同的含义。函数重载是C++多态的特性(静态多态),用同一个函数名代表不同的函数功能。

2.2、函数重载的条件

同一作用域,函数的参数类型不同、个数不同、顺序不同都可以重载。返回值类型不能作为重载的条件。

#include <iostream>
using namespace std;
void func(int x)
{
	cout<<"int"<<endl;
}
void func(char x)
{
	cout<<"char"<<endl;
}
void func(int x,char y)
{
	cout<<"int char"<<endl;
}
void func(char x, int y)
{
	cout<<"char int"<<endl;
}
void func(double x)
{
	cout<<"double"<<endl;
}

int main()
{
	func(100);
	func(100,'y');
	func('y');
	func('y',100);
	func(100.2);
	return 0;
}

输出:

int
int char
char
char int
double

为什么函数返回值不能作为重载条件?
这是因为函数调用时实参和形参的匹配过程是在编译期完成的,而编译器无法确定一个表达式的返回类型,因此不能以返回值类型作为重载条件。

比如以下示例:

int add(int a, int b) { return a + b; }
float add(int a, int b) { return (float)a + (float)b; }

如果允许通过返回值类型来区分同名函数,那么上述代码就合法了。但问题是,在调用add(1,2)时,编译器无法确定应该选择哪个函数。因为传入的参数都是int型,可以被两个函数接受,并且两个函数的返回值也都可以转换成其他数据类型。这样就会产生二义性错误。
因此,在C++中必须使用参数列表来区分同名函数,禁止使用返回值作为重载的条件。

2.3、函数重载的底层实现原理

C++中的函数重载是通过名称修饰(Name Mangling)实现的。当我们定义一个函数时,编译器会将函数名和参数列表一起进行名称修饰,生成一个新的、唯一的符号名称,这个符号名称就是该函数在目标代码中的标识。

也就是说,对于每个函数,在编译期间都会生成一个完整的符号名。这个符号名包含了函数名、参数类型以及参数数量等信息。例如,下面是两个重载函数的原型:

void func(int a, int b);
void func(double x, double y);

在编译期间,它们会被分别转换为以下两个不同的符号名:

_Z4funcii
_Z4funcdd

这些符号名使用特定的命名规则来保证它们在链接时可以正确地解析成相应的函数。

当调用一个重载函数时,编译器会根据传递给它的实参列表确定需要调用哪个版本。如果实参能够与某个重载版本匹配,则选择该版本进行调用;否则就产生错误。

C++中的函数重载本质上是一种静态多态性机制。它通过在编译期间对不同形式的同名函数进行名称修饰来区分不同版本,并且可以根据传递给它们的实参类型和数量进行选择。

不同的编译器可能会产生不同的内部名。

三、函数的默认参数

C++在什么函数原型的时候可以为一个或多个参数指定默认的参数值,当函数调用的时候如果没有指定这个值,编译器自用默认值代替。

注意点:

  1. 如果一个形参设置了默认参数值,那么它后面位置的形参也需要设置默认参数值。
  2. 如果函数声明和函数定义分开,函数声明设置了默认参数,函数定义不能再设置默认参数。
  3. 默认参数和函数重载同时出现一定要注意二义性。
#include <iostream>
using namespace std;
void func(int a=0,int b=100,int c=200)
{
	cout<<a<<" "<<b<<" "<<c<<endl;
}
// 如果一个形参设置了默认参数值,那么它后面位置的形参也需要设置默认参数值。
void func01(int a,int b=0,int c=100)
{
	cout<<a<<" "<<b<<" "<<c<<endl;
}
// 如果函数声明和函数定义分开,函数声明设置了默认参数,函数定义不能再设置默认参数。
void func02(int a,int b=0,int c=100);
void func02(int a,int b,int c)
{
	cout<<a<<" "<<b<<" "<<c<<endl;
}
int main()
{
	// 没有传参,使用默认参数
	func();
	// 如果传入一个参数,那么第二个及以后的参数使用默认参数
	func(100);
	// 如果传入两个参数,那么第三个及以后的参数使用默认参数
	func(100200);

	return 0;
}

输出:

0 100 200
100 100 200
100 200 200

默认参数和函数重载同时出现一定要注意二义性。

#include <iostream>
using namespace std;
void func(int a,int b)
{
	cout<<a<<" "<<b<<endl;
}
void func(int a,int b=100)
{
	cout<<a<<" "<<b<<endl;
}

int main()
{
	func(100,200);//OK
	func(10);// error,产生二义性
	return 0}

四、占位参数

C++声明函数时,可以设置占位参数。占位参数只有参数类型,而没有参数名。 一般情况下,在函数体内部无法使用占位参数。另外,占位参数也可以设置默认值;没有设置默认值的占位参数的函数在调用时必须拥有一个实参。

#include <iostream>
using namespace std;
void func01(int a,int b,int)
{
	// 函数内部无法使用占位参数
	cout<<a<<" "<<b<<endl;
}
void func02(int a,int b,int =100)
{
	// 函数内部依旧无法使用占位参数
	cout<<a<<" "<<b<<endl;
}

int main()
{

	func01(100,200);// error, 错误调用,占位参数也是参数,必须传参数
	func01(100,200,300);// OK
	func02(100,200);// OK
	func02(100,200,300);// OK
	return 0}

占位参数可用于操作符重载的后置++的时候。

五、extern "C"浅析

在C++中,可以使用extern "C"来声明一个函数或变量是以C语言的方式进行编译和链接的。这个关键字通常用于在C++程序中调用C语言库的函数或变量。

当使用extern "C"修饰函数时,编译器会禁止对该函数进行名称重整(Name Mangling),也就是说,它不会将函数名加上类似于参数类型、返回值类型等标志信息作为后缀来生成独一无二的符号名。这样,在链接时就可以找到与之匹配的C语言库中的函数。

通俗的讲:
c函数void myFunc(){}被编译成函数myFunc;而C++函数void myFunc(){}被编译成函数_Z6myFuncv;这是由于C++需要支持函数重载,所以c和C++中对同一个函数经过编译后生成的函数名是不相同的;这就导致一个问题,如果在C++中调用一个使用c语言编写模块中的某个函数,那么C++是根据C++的名称修饰方式来查找并链接这个函数,这就会发生链接错误。
比如上面举的例子,C++中调用C语言实现的myFunc函数,在链接阶段会去找_Z6myFuncv,结果是找不到,因为void myFunc(){}函数是C语言编写的,生成的函数名是myFunc。那么如果想在C++调用c的函数怎么办?extern "C"的主要作用就是为了实现C++代码能够调用其他C语言代码,加上extern "C"后,这部分代码编译器按照c语言的方式进行编译和链接,而不是按照C++的方式。

使用extern "C"的格式:

#if __cplusplus
extern "C"{
#endif
	//函数声明
	//...
#if __cplusplus
}
#endif

使用示例:
func.h

#ifndef _FUNC_H_
#define _FUNC_H_

#if __cplusplus
extern "C" {
#endif
	int func(int a, int b);

#if __cplusplus
}
#endif

#endif // !_FUNC_H_


func.c

#include <stdio.h>
#include "func.h"
int func(int a,int b)
{
	printf("c func\n");
	return a + b;
}

mian.cpp

#include "func.h"
int main()
{
	func(100, 200);
	return 0;
}

总结

  1. 内联函数。内联函数是指在编译时将函数调用处直接替换为函数体的代码,从而提高程序的执行效率。可以使用关键字inline来声明一个内联函数。

  2. 函数重载。函数重载是指在同一作用域中定义多个名称相同但参数个数或类型不同的函数。通过函数重载可以简化代码并提高可读性。

  3. 函数的默认参数。函数的默认参数是指在定义函数时给某些参数赋予默认值,在调用该函数时如果没有传递这些参数,则会自动使用默认值。

  4. 占位参数。占位参数是指在定义函数时声明了一个没有名字的参数,只起到占位符的作用,不能被实际使用。通常用于需要传入一定数量的参数,但是具体值在调用时才确定的情况下。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/640945.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32单片机TFT显示AD9833 DDS信号发生器语音播报正弦波方波三角波

实践制作DIY- GC0146---TFT显示AD9833 DDS信号发生器 基于STM32单片机设计---TFT显示AD9833 DDS信号发生器 二、功能介绍&#xff1a; 硬件组成&#xff1a;STM32F103C系列最小系统板 1.8寸TFT彩屏AD9833信号模块4*4矩阵键盘DY-SV17F语音播报模块 1.通过4*4键盘来设定频率值和…

ExpertLLaMA:超越Vicuna,通过角色扮演增强指令,显著提升回答质量

本文向大家介绍我们刚刚开源的对话模型及相应的训练数据。 首先是 git Repo 和 paper 链接&#xff0c;欢迎大家给我们⭐star⭐ 论文标题&#xff1a; ExpertPrompting: Instructing Large Language Models to be Distinguished Experts 论文链接&#xff1a; https://arxiv.or…

.Net7矢量化的性能优化

前言 矢量化是性能优化的重要技术&#xff0c;也是寄托硬件层面的优化技术。本篇来看下。文章来源&#xff1a;微软官方博客 概括 一&#xff1a;矢量化支持的问题&#xff1a; 矢量化的System.Runtime.Intrinsics.X86.Sse2.MoveMask 函数和矢量化的Vector128.Create().Extract…

get 、post请求 后台@RequestParam、@RequestBody 接收的方法集合

post、get请求 参数&#xff1a;数组、JSON对象、JSON字符串、地址栏 RequestParam、RequestBody 后端接收 //定义json对象&#xff0c;同时包含数组 var _queryData { jflb:"婚姻家庭纠纷",zlay:"xxxxx",ysCode:["0123","4567"]}…

机柜PDU与普通插座的区别,以及如何选择品牌专业PDU产品详解

PDU&#xff08;Power Distribution Unit&#xff09;&#xff0c;是将来自UPS的输出电流分配到各个IT设备的末端配电设备&#xff0c;是连接供电等基础设施与IT系统、关联机房内所有设备正常运转的关键设备。作为机房用电安全的重要保障&#xff0c;PDU设备的稳定与安全直接关…

FPGA驱动FT601实现USB3.0相机HDMI视频采集 提供工程源码和QT上位机源码

目录 1、前言2、FT601芯片解读和时序分析FT601功能和硬件电路FT601读时序解读FT601写时序解读 3、我这儿的 FT601 USB3.0通信方案4、详细设计方案5、vivado工程详解6、上板调试验证7、福利&#xff1a;工程代码的获取 1、前言 目前USB3.0的实现方案很多&#xff0c;但就简单好…

我们来谈谈udp

"却还有那些洗礼&#xff0c;那几句问候&#xff0c;那份温柔~" 一、 常用的Linux命令 (1) netstat查看网络状态 netstat是一个用来查看网络状态的重要工具,可以携带很多选项。 n 拒绝显示别名&#xff0c;能显示数字的全部转化成数字. l 仅列出有在 Listen…

【unity之IMGUI】所以你还想在百度上搜IMGUI的底层原理是什么吗?

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

安装python详细步骤(超详细,保姆级,一步一图)

❄️作者介绍&#xff1a;奇妙的大歪❄️ &#x1f380;个人名言&#xff1a;但行前路&#xff0c;不负韶华&#xff01;&#x1f380; &#x1f43d;个人简介&#xff1a;云计算网络运维专业人员&#x1f43d; 前言 作为一个实用学习的主义的学习者&#xff0c;最关心的问题一…

光亚展 | 移远通信Matter解决方案,照亮智能家居产业未来

6月9-12日&#xff0c;第二十八届广州国际照明展览会&#xff08;光亚展&#xff09;在中国进出口商品交易会展馆举行。本次展会以“光未来”为主题&#xff0c;整个照明、灯饰产业链的上下游企业、品牌在此汇聚&#xff0c;共同探讨照明行业未来的发展方向。 作为照明行业智能…

智见|黄铁军:未来的大模型生态中将会只有少数赢家

2023智源大会可谓群星璀璨。中外200余位人工智能顶级专家参会&#xff0c;人工智能领域最关键的人物、机构悉数亮相。 全面、专业、前沿&#xff0c;会场上大咖们观点激荡、多元碰撞&#xff0c;会场下观众们兴奋异常、座无虚席。 会上&#xff0c;北京智源人工智能研究院院长黄…

英语知识点-填空-考试酷

第一章 英语知识填空题 一、语法 1.词法&#xff1a;介词将关系建立在人/物A与人/物B&#xff0c;可能会形成修饰限制关系&#xff0c;有定语成分&#xff1b;介词将关系建立在事A与物B&#xff0c;可能有事A动作发生时环境因素&#xff0c;有状语成分&#xff1b;写作中经常用…

【Vue.js】1711- 深入浅出 Vue3 自定义指令

Vue.js[1] 提供了丰富的指令来简化开发者的工作。除了内置指令外&#xff0c;Vue.js 还支持自定义指令&#xff0c;开发者可以根据自己的需求扩展 Vue.js 的指令库。Vue.js 3.x 相较于 Vue.js 2.x 在自定义指令方面进行了一些改进&#xff0c;本文将介绍 Vue.js 3.x 中自定义指…

HLS 设计数字时钟

绪论 该项目的目标是展示 HLS 在设计数字系统方面的能力。为此&#xff0c;本文展示如何在 HLS 中描述数字时钟。如果有兴趣学习 HLS 编码技术&#xff0c;请参阅&#xff1a; ❝ https://highlevel-synthesis.com/ ❞ ❝ https://www.udemy.com/course/hls-combinational-circ…

2023 年的 5G 和网络安全风险

5G 网络的推出出奇地缓慢。作为一个概念&#xff0c;它于 2016 年推出&#xff0c;但直到 2019 年才在全球范围内推出。 四年后&#xff0c;在大多数国家地区&#xff0c;拥有 5G 设备的人数仍然很少。 不确定采用缓慢背后的原因是负担能力、缺乏必要性还是关于它的严重错误…

【redis】redis集群

这里是redis系列文章之《redis集群》&#xff0c;上一篇文章链接&#xff1a;【redis基础】哨兵_努力努力再努力mlx的博客-CSDN博客 目录 概念 作用 集群算法-分片-槽位slot 槽位与分配的概念及两者的优势 官网介绍分析 槽位 分片 两者的优势 slot槽位映射的三种解决方…

linux eventfd事件通知 比信号量更好用

专栏内容&#xff1a;linux下并发编程个人主页&#xff1a;我的主页座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物&#xff0e; 目录 前言 概述 原理简介 使用场景 接口说明 头文件 参数说明 代码演示 默认参数 …

1740_使用Python+ImageMagick实现图像的批量压缩

全部学习汇总&#xff1a; GreyZhang/python_basic: My learning notes about python. (github.com) 前些年使用Linux的时候为了能够方便地往网络上上传照片&#xff0c;使用shell ImageMagick的组合进行照片的批量压缩一直觉得比较方便。不过&#xff0c;那时候即使这么简单的…

JMeter从入门到精通--开始你的第一个JMeter脚本

JMeter是一款在国外非常流行和受欢迎的开源性能测试工具&#xff0c;像LoadRunner 一样&#xff0c;它也提供了一个利用本地Proxy Server&#xff08;代理服务器&#xff09;来录制生成测试脚本的功能&#xff0c;但是这个功能并不好用。所以在本文中介绍一个更为常用的方法——…

软考A计划-2023系统架构师-知识点集锦(4/4)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…