C++内联函数与引用(超详细)

news/2024/5/20 4:11:22 标签: c++, 开发语言, 内联函数, 引用

文章目录


前言

一、内联函数

1.为什么会存在内联函数

🧐🧐首先我们介绍内联函数之前,首先看一下我们之前学过的#define 定义的宏

写一个宏函数,实现两个函数的相加

#include <iostream>
using namespace std;

#define ADD(x,y) ((x)+(y))
int main()
{
	int a = 10;
	int b = 20;
	cout << ADD(a,b) << endl;
	return 0;
}

想想我们当初学习宏函数的的优点和缺点
⚾⚾优点:这些函数我们也可以用函数来实现,但是函数调用需要开辟栈帧,浪费空间,
而宏就完美解决了这个问题,它会在调用它的地方展开。进行完全替换。
⚾⚾缺点:不能调试,使用起来复杂,没有安全类型的检查,代码可读性差

那我们可不可以实现一个专门的库函数,既包含宏的有点又避免了函数调用的缺点呢??
这时内联函数就出现了!!!

2.什么是内联函数

✨ ✨概念:用inline修饰的函数就是内联函数,编译时C++编译器会在调用的地方展开,没有函数调用建立栈帧的开销,提高程序运行效率。

int add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 10;
	int b = 20;
	int ret = add(a, b);
	cout << ret<< endl;
	return 0;
}

我们转到反汇编看一下这段代码

在这里插入图片描述

在正常情况下,没有加内联函数确实进行了函数调用建立栈帧。

我们加上内联函数再来看一下

在这里插入图片描述
我们发现怎末跟上面代码一样,都调用了add函数,建立了栈帧。这厮怎末回事???

这是因为在Debug模式下,方便调试,编译器默认不进行优化,也就是不进行展开,依然使用一般的函数调用。
我们对vs设置一下环境
在这里插入图片描述
可以看到,优化之后inline修饰过的函数会直接展开。

也就是说,在Debug模式下你写代码、调试代码时,inline 相当于普通函数,可以进行调试;在release模式下,又可直接进行原地展开,提高效率(这里指的是避免了调用函数、创建栈帧等方面的资源消耗)

在这里插入图片描述

3.内联函数注意事项

🎇🎇内联函数是一种以空间换时间的方法,如果编译器将函数当成内联函数处理,在编译阶段,会当成函数体替换来调用。
缺陷:函数体目标变大
优势:少了调用开销,提高程序运行效率

🎇🎇内联函数对于编译器来说,只是一个建议,不同编译器关于inline实现机制可能不同,一般建议函数规模较小的,不是递归实现,调用频繁的采用内联函数修饰,否则编译器会忽略inline的特性

🎇🎇inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

main.cpp

#include "add.h"
int main()
{
	int ret = add(10, 20);
	cout << ret<< endl;
	return 0;
}

add.cpp

#include "add.h"
inline int add(int x, int y)
{
	return x + y;
}

add.h

#include <iostream>
using namespace std;

inline int add(int x, int y);

严重性 代码 说明 项目 文件 行 禁止显示状态
错误 LNK2019 无法解析的外部符号 “int __cdecl add(int,int)” (?add@@YAHHH@Z),函数 main 中引用了该符号 12_29 D:\study C\c嘎嘎\12_29\12_29\main.obj 1

我们一般建议把内联函数放在一个文件夹中进行处理

#include <iostream>
using namespace std;
inline int add(int x, int y)
{
	return x + y;
}
int main()
{
	int ret = add(10, 20);
	cout << ret<< endl;
	return 0;
}

二、引用

1.什么是引用

其实引用就是给变量取别名,这个别名和这个变量共用同一块空间。

使用:类型名 &别名=变量名; 对别名的改变同样也改变变量名的值

在这里插入图片描述

引用只能用于同类型变量才可以

在这里插入图片描述

2.引用的特性

引用时必须初始化

在这里插入图片描述

可以给一个变量取多个别名,也可以给别名起别名

在这里插入图片描述

引用一旦引用了一个实体,就不可以在引用其他实体

在这里插入图片描述

3.常引用

我们对引用加上const修饰后会发生什么呢???

权限不可以扩大,本来是const int 类型,现在变成了int类型

在这里插入图片描述
那我们如何进行修改呢??
只要在引用前边加上const就可以

这其实就是权限的平移,本来是一个const int 类型,现在变成了const int 。两个权限一致

在这里插入图片描述

深度理解临时变量

我们看一下这段代码
在这里插入图片描述

我们发现他出错了,但是为什么会出错呢??
有点人感觉这是类型不匹配导致的!!但其实并不是这样,他还有更深层次的理解

隐式类型转化,强转,截断都会发生产生临时变量,这个临时变量具有常性(不可被修改)

举个例子::
int a=10;double b=a;
这段代码肯定不会报错,会正常运行,但是他为什么可以实现呢???
本质是编译器把a那个变量进行了拷贝,产生了临时变量,通过这个临时变量取进行类型转化✨✨✨✨

权限既然不能放大,那我们可不可以缩小呢??
这个其实是可以的,我们来具体看一下

在这里插入图片描述

4.引用使用场景

作为函数参数,进行传参,避免指针的使用错误

我们来回忆一下之前c语言通过一个函数对两个变量进行交换,我们的形参必须用指针才可以实现,是不是很麻烦,那这时我们就可以用引用来实现了!!!

在这里插入图片描述
在这种场景下我们用引用就会非常方便

作为函数参数,进行传参,避免拷贝,提高程序运行效率

想想我们当时为什么会用指针来进行传参,比如我们传结构体传参,会生成一份原大小的拷贝,对资源浪费非常大。指针传参仅仅会拷贝4/8个字节,减少了空间的浪费。那我们也可以用引用来进行传参,南无连这个4/8个字节的拷贝也不需要了。
在这里插入图片描述

作为返回值,进行传参,避免拷贝,提高程序运行效率

我们知道调用函数,我们并不是返回的那个对象,而是那个对象那个的拷贝,我们也可以通过进行引用返回避免拷贝

在这里插入图片描述

作为返回值,我们还可以对返回值进行修改,做返回值如果出了作用于还在,可以用传引用返回,如果出了作用域就不在了(销毁了),那就不能用传引用返回,只能用传值返回

5.引用与指针

在语法概念上,我们知道,引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。

在这里插入图片描述

但是在底层上,我们会发现,引用也是用指针实现的

我们转到反汇编观察一下
在这里插入图片描述

🌟指针存储一个变量地址,而引用是一个变量的别名
🌟引用必须初始化,指针没有要求
🌟引用一旦引用了一个实体,就不可以再引用其他实体。地址可以在任何时候指向一个
同类型的实体
🌟只有空指针,没有空引用
🌟只有多级指针,没有多级引用
🌟引用+1是指向的变量大小进行加一,指针+1是跳过一个指向类型的大小的地址
🌟引用大小为引用实体的大小,而指针的大小是4/8字节
🌟访问方式不同,指针需要解引用访问,引用编译器自己处理
🌟引用使用起来比指针相对安全

总结

以上就是今天要讲的内容,本文仅仅详细介绍了C++内联函数引用,希望对大家的学习有所帮助,仅供参考 如有错误请大佬指点我会尽快去改正 欢迎大家来评论~~ 😘 😘 😘 😘


http://www.niftyadmin.cn/n/5296041.html

相关文章

17. Mysql 动态SQL

概述 动态 sql 是指在程序运行时根据不同的条件和需求生成不同的 sql 语句。通过动态 sql&#xff0c;我们可以实现灵活的查询、更新和删除操作&#xff0c;提高代码的复用性和可维护性。 应用场景&#xff1a; 动态查询&#xff1a;根据用户输入的条件动态生成查询语句&…

React实现抽屉组件

简介 本文将会基于react实现简单的抽屉组件功能&#xff0c;当列表页点击时候&#xff0c;会显示抽屉详情。 Drawer.js 抽屉组件&#xff0c;通过父组件传递isOpen状态来开启或关闭抽屉。 export const Drawer ({isOpen, children}) > {const [visible, setVisible] u…

详解C语言入门程序:HelloWorld.c

#include <stdio.h> // 头文件&#xff0c;使用<>编译系统会在系统头文件目录搜索在C语言中&#xff0c;#include 是预处理指令&#xff0c;用于将指定的头文件内容插入到当前源文件中。这里的 <stdio.h> 是一个标准库头文件&#xff0c;其中包含了与输入输出…

【力扣100】207.课程表

添加链接描述 class Solution:def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:# 思路是计算每一个课的入度&#xff0c;然后使用队列进行入度为0的元素的进出# 数组&#xff1a;下标是课程号&#xff0c;array[下标]是这个课程的入度# 哈希…

微信商家转账到零钱开通技巧,模板下载

商家转账到零钱是什么&#xff1f; 【商家转账到零钱】功能整合了微信支付之前的【企业付款到零钱】【批量转账到零钱】功能&#xff0c;支持批量对外转账&#xff0c;对有批量对用户付款需求的应用场景更友好&#xff0c;操作便捷。如果你的应用场景是单付款场景的话&#xf…

numpy数组04-数组的轴和读取数据

一、数组的轴 在numpy中数组的轴可以理解为方向&#xff0c;使用0&#xff0c;1&#xff0c;2...数字表示。 对于一个一维数组&#xff0c;只有一个0轴&#xff0c;对于2维数组&#xff08;如shape&#xff08;2&#xff0c;2&#xff09;&#xff09;&#xff0c;有0轴和1轴…

R306指纹识别模块的硬件接口

1.外部接口尺寸图 采集芯片外形尺寸&#xff1a;33.4*20.4*3.79 mm 2.串行通讯 R306 指纹模块通讯接口定义&#xff1a; 3.USB 通讯 4.接口说明 4.1 UART a) UART 缺省波特率为 57.6kbps&#xff0c;数据格式&#xff1a;8 位数据位&#xff08;低位在前&#xff09;&#…

PLC龙门刨床横梁运动控制程序示例

一、龙门刨床横梁运动控制 二、按钮信号地址分配 三、对应的程序梯形图 1、首先按下上升按钮&#xff0c; 同时接通这两个触点&#xff08;上升之前先对横梁进行放松&#xff09; 2、当放得足够松的时候&#xff0c;放松通路断开&#xff0c;上升通路接通&#xff0c;横梁开始上…