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

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

量化投资 现代投资组合理论(MPT)

量化投资 现代投资组合理论&#xff08;MPT&#xff09; 问题&#xff1a;构建投资组合&#xff0c;达到目标收益率的同时拥有最小的 risk exposure. 有 J J J 个可交易证券&#xff0c;期望收益率为 R [ R 1 , ⋯ , R j ] T R[R_1,\,\cdots,\,R_j]^T R[R1​,⋯,Rj​]T&…

监控、审计和运行时安全

监控、审计和运行时安全 目录 文章目录 监控、审计和运行时安全目录1、分析容器系统调用&#xff1a;SysdigSysdig介绍安装sysdigsysdig常用参数sysdig常用命令Chisels(实用的工具箱)其它命令 2、监控容器运行时&#xff1a;FalcoFalco介绍Falco架构安装falco自定义扩展规则文件…

千万级入口服务[Gateway]框架设计(一)

本文将以技术调研模式编写&#xff0c;非技术同学可跳过。 文章目录 背景问题[不涉及具体业务]目标技术选型语言框架模式实现一&#xff1a;go 原生组件Demo 实现Benchwork 基准性能小结实现二&#xff1a;开源 go-plugin 附录入口服务演变 背景 在历史架构的迭代中&#xff…

Apache Kafka学习

目录 一、简介 1.概念&#xff1a; 2.kafka四大API&#xff1a; 3.Kafka消费模式 4.Kafka的基础架构 5.kafka文件存储方式 二、特性 三、优点 1.解耦 2.异步处理 3.流量削峰 4.数据持久化 5.顺序保证 6.可恢复性 四、名词解释 五、QA Q:如何保证数据高可靠、不…

从美颜算法到AI美颜SDK:美丽的背后隐藏着什么?

在年轻人的生活中&#xff0c;通过美颜SDK类型的美颜工具进行拍摄已经成为了一种全新的文化现象。时下&#xff0c;AI美颜、美颜SDK讨论热点极高&#xff0c;那么大家知道美颜算法和AI美颜到底有什么不同吗&#xff1f;它们背后隐藏着什么样的技术和思想&#xff1f; 一、美颜算…

在Windows11平台安装JDK11(双11)

目录 引言一、安装前说明1.系统要求2.多版本安装 二、JDK11安装三、安装成功验证1.验证2.Path环境变量 总结 引言 本文主要是详细讲解在 Windows 11 系统上安装 JDK 11&#xff0c;安装时有一些注意事项需要说明。与 JDK 8 的安装过程有少许不一样。 一、安装前说明 1.系统要…

Atair 柱状比例图

如何熟练掌握可视化库和应对使用过程的疑难问题&#xff1f; 基本用法不妨访问 GeeksforGeeks 疑难问题优先搜索 https://stackoverflow.com 尽量使用官方文档&#xff1a; numpy的学习访问 https://numpy.org/doc/stable/user/index.html 例如&#xff1a; 一则 altair 使用过…

小程序中半屏打开其他小程序,开发者工具调试半屏

前言&#xff1a; 有需要是在当前小程序中&#xff0c;点击操作时&#xff0c;如果他没有注册会员&#xff0c;则强制去另一个小程序去注册会员&#xff0c;注册成功在返回&#xff0c;在这期间&#xff0c;打开另一个小程序是半屏来展示的。 实现效果&#xff1a; 在a小程序中…

阿里4年测试经验分享 —— 测试外包干了3年后,我废了...

去年国庆&#xff0c;我分享了一次一位阿里朋友的技术生涯&#xff0c;大家反响爆蓬&#xff0c;感觉十分有意思&#xff0c;今天我来分享一下我另一位朋友的真实经历&#xff0c;是不是很想听&#xff1f; 没错&#xff0c;我这位朋友是曾经外包公司的测试开发&#xff0c;而…

React新版扩展特性

目录 Hooks 三个常用的Hook State Hook Effect Hook Ref Hook Context Router 6 声明式路由 编程式路由导航 Hooks (1) Hook是react 18.8.0版本新增的特性/语法 (2) 可以让我们在函数式组件中使用state以及其他的react特性 三个常用的Hook (1) State Hook: React.useSt…

使用javacv中的ffmpeg实现录屏

今天突发奇想&#xff0c;想自己写一个录屏的软件&#xff0c;上次写了一个专门录音的Demo&#xff0c;但是要把声音和视频放到一起合成一个mp4文件&#xff0c;着实有一点艰难&#xff0c;所以就打算使用ffmpeg来写一个&#xff0c;而这篇博客中会顺便谈一谈我碰到的各种坑。 …

HarmonyOS学习路之开发篇—Java UI框架(StackLayout)

StackLayout StackLayout直接在屏幕上开辟出一块空白的区域&#xff0c;添加到这个布局中的视图都是以层叠的方式显示&#xff0c;而它会把这些视图默认放到这块区域的左上角&#xff0c;第一个添加到布局中的视图显示在最底层&#xff0c;最后一个被放在最顶层。上一层的视图…

数据湖仓一体化架构:探究新一代数据处理的可能性

一、引言 随着大数据的快速发展&#xff0c;企业不断寻求高效、灵活和经济的方法来处理和管理海量数据。在这种背景下&#xff0c;数据湖和数据仓库这两种不同的架构模式各自展现出其独特的优势。而数据湖仓一体化架构&#xff0c;是对这两种模式优势的综合&#xff0c;为企业…

wenda+fess问答系统

1 安装conda 2 创建环境 conda activate --name wenda python3.8 3 安装依赖工具包 pip install -r requirements/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple pip install torch BeautifulSoup4 torchvision torchaudio pdfminer.six -i https://pypi.t…

【乐观锁与悲观锁】—— 每天一点小知识

&#x1f4a7; 乐观锁与悲观锁 \color{#FF1493}{乐观锁与悲观锁} 乐观锁与悲观锁&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞见云的博客&#x1f390; &#x1f433; 《数据结构与算法》专栏的文章图文并茂&…

美团买菜基于 Flink 的实时数仓建设

摘要&#xff1a;本文整理自美团买菜实时数仓技术负责人严书&#xff0c;在 Flink Forward Asia 2022 实时湖仓专场的分享。本篇内容主要分为四个部分&#xff1a; 背景介绍 技术愿景和架构设计 典型场景、挑战与应对 未来规划 点击查看原文视频 & 演讲PPT 一、背景介绍…

设置全局loading

为什么要设置全局loading&#xff1f; 在项目开发过程中&#xff0c;请求接口的时候延迟没有数据&#xff0c;页面感觉狠卡顿&#xff0c;这个时候就要用loading来做一个延迟界面。 但是每个界面都写loading的话就会很复杂&#xff0c;所以今天给大家带来了一个全局loading的…

JMeter+Ant+jenkins搭建接口自动化测试环境

目录 前言&#xff1a; 1.ant简介 2. 构建ant环境 3.JMeter与Ant集成 4. 报告优化 5.jenkins持续集成 前言&#xff1a; JMeter是一个开源的性能测试工具&#xff0c;可以用于测试Web应用程序或API接口的性能&#xff0c;支持多种通信协议和数据格式。Ant是一个构建工具&…

基于卫星星历计算卫星在CGCS2000大地坐标系中的坐标

目录 一、北斗系统概述 1.空间星座 2.坐标系统 3.时间系统 二、实验目的 三、实验内容 四、实验过程 五、实验结果 一、北斗系统概述 1.空间星座 北斗卫星导航系统简称北斗系统&#xff0c;英文缩写为 BDS&#xff0c;其空间星座由 5 颗地球静止轨道&#xff08;GEO&…

学会使用这些Lumion照片级渲染技巧,秒出大片

Lumion 是一种渲染软件&#xff0c;可帮助建筑师以清晰、感性的方式传达他们的设计。十年来&#xff0c;人们发现 Lumion 的每个新版本都有新的功能、工作流程和控制方法。他们可以在 Revit、SketchUp 或其他 BIM 程序等软件中建模&#xff0c;并将模型导入 Lumion 进行渲染&am…