iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++ 引用与内联函数详情
  • 520
分享到

C++ 引用与内联函数详情

2024-04-02 19:04:59 520人浏览 独家记忆
摘要

目录引用初阶什么是引用为何要有引用引用指向同一块空间引用的特性定义时必须初识化一个变量可以多次引用引用一旦引用了一个实例,不能在再引用其他的实例引用进阶常引用权限临时变量具有常属性引

引用初阶

引用是c++的特性的之一,不过C++没有没有给引用特意出一个关键字,使用了操作符的重载。引用在C++中很常见,常见就意味着它很重要。我们分两个境界来谈谈引用,初阶是我们能在书上看到的.

什么是引用

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间 .所谓的取别名,就是取外号,张三在朋友面前可能叫三子,在长辈面前可能叫三儿,但是无论是叫三子还是三儿,他们叫的就是张三,这是无可否认的.引用的符号&和我们取地址的&操作符一样的,后面我们就会知道这是操作符构成重载的原因.

我们可以理解所谓的引用.

#include <stdio.h>
int main()
{
	int a = 10;
	int& b = a;       //  b 是 a的一个别名
	printf("%p\n", &a);
	printf("%p\n", &b);
	return 0;
}

为何要有引用

引用有很多优点,其中有一个就是可以简化代码,我们在C语言中写过如何交换两个整型数据,需要借助一维指针,但是这有一点麻烦,使用引用就可以很好的解决这个问题.

#include <stdio.h>
void swap(int& a, int& b)
{
	int c = a;
	a = b;
	b = c;
}
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前  a = %d b = %d\n", a, b);
	swap(a, b);
	printf("交换后  a = %d b = %d\n", a, b);
	return 0;
}

引用指向同一块空间

我们无论给变量取多少外号,但是这些外号所指向的空间是一样的,我们只要修改一个外号的空间,就会导致其他外号值得修改.

#include <iOStream>
using namespace std;
int main()
{
	int a = 10;
	int& b = a;  //b 是  a  的别名
	int& c = b;  //还可以对c取别名
	return 0;
}

int main()
{
	int a = 10;
	int& b = a;  //b 是  a  的别名
	int& c = b;  //还可以对c取别名
	c = 20;
	cout << "a: " << a << endl;
	cout << "b: " << a << endl;
	cout << "c: " << a << endl;
	return 0;
}

引用的特性

即使引用很好使用,但是我们还要关注他们的一些特性.

定义时必须初识化

这个要求很严格,我们必须在引用的时候给他初始化,否则就会报错.

#include <iostream>
using namespace std;
int main()
{
	int a = 10;
	int& b;
	return 0;
}

一个变量可以多次引用

这个特性我们前面和大家分享过了,我们可以对一个变量多次取外号,每一个外号都是是这个变量的别名,我们甚至可以对外号取外号,不过我们一般不这么干.

引用一旦引用了一个实例,不能在再引用其他的实例

这个特性完美的阐释了引用的专一性,一旦我们给某一个变量起了一个别名,这个别名就会跟着这个变量一辈子,绝对不会在成为其他的变量的别名.这个也是和指针很大的区别,指针比较花心.

这个我们就不通过代码打印各个变量的地址了.,我们观看另外一种现象,通过观察我们看到a的值也被修改了,可以确定 b 仍旧a的别名.

int main()
{
	int a = 10;
	int& b = a;
	int c = 20;
	// 这个一定是赋值
	b = c;
	cout << "a: " << a << endl;
	return 0;
}

引用进阶

我们已经看到引用的优点,但是这是引用的基础用法,要是到那里,我们一般看书都可以做到,现在要看看引用更加详细的知识.

常引用

我们在C语言中,专门分享过const相关的知识,那么我们如何对const修饰的变量取别名呢?这是一个小问题.

const int a = 10;

我们看看下面的方法.

int main()
{
	const int a = 10;
	int& b = a;        //报错
	const int& c = a;  // 不报错
	return 0;
}

到这里我们就可以发现,我们取别名的时候也用const修饰就可以了,这是我们从现象中的得出的结论,但是这又是因为什么呢?我们需要知道他们的原理.

权限

不知道大家有没有在学习中看到过这样一种现象,对于一个文件,我们可能存在仅仅阅读的权限,自己无法修改,但是其他的人有可能有资格修改,这就是权限的能力,const修饰变量后,使得变量的加密程度更加高了,我们取别名的时候,总不能把这个权限给过扩大了,编译器这是不允许的,也就是说我们取别名的时候,权限只能往低了走,绝对不能比原来的高.

下面就是把权限往缩小了:

int main()
{
    int a = 10;
	const int& b = a;
	return 0;
}

常量取别名必须要是const修饰的,因为常量不能修改.

int main()
{
	const int& a = 10;
	return 0;
}

临时变量具有常属性

这个是语法的一个知识点,我们记住就可以了,现在我们就要看看为什么这个代码会可以运行.

我们常引用 还有最后一个问题,如果我们是double类型的变量,如何给取一个int类型的别名?

需要用const修饰,而且会发生截断:

int main()
{
	double d = 1.2;
	const int& a = d;       //需要用const修饰
	cout << a << endl;
	return 0;
}

它的本质不是取别名,而类似于给常量取别名,而且还会发生截断.这个说截断也不太合适,这会产生一个临时变量,我把原理图放在下面.

int main()
{
	double d = 1.2;
	const int& a = d;
	cout << "&a :" <<&a << endl;
	cout << "&d :" <<&d << endl;
	return 0;
}

引用的场景

我们需要来看看引用的使用场景,它主要有两大作用.

  • 做参数
  • 做返回值

做参数

我们可以使用引用来交换两个整型数据:

#include <stdio.h>
void swap(int& a, int& b)
{
	int c = a;
	a = b;
	b = c;
}
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前  a = %d b = %d\n", a, b);
	swap(a, b);
	printf("交换后  a = %d b = %d\n", a, b);
	return 0;
}

但是使用引用作为参数可能会出现权限不太匹配的错误,所以说我们需要const修饰.

下面就是由于权限问题,我们没有办法来给常量却别名,这就需要const修饰.

void func(int& a)
{
}
int main()
{
	int a = 10;
	double b = 10.2;

	func(a);
	func(10);
	func(b);
	return 0;
}

返回值

我们先看看返回值的原理,再说说引用做返回值.

返回值的原理:

我们需要谈谈编译器是如何使用返回值的,以下面的代码为例.

int func()
{
	int n = 1;
	n++;
	return n;
}

int main()
{
	int ret = func();
	return 0;
}

编译器会看这个返回值的空间大不大,如果不大,就把的数据放到一个寄存器中,如果很大,看编译器的机制,有的编译器甚至可能在main函数中开辟这个空间来临时保存数据.

这是由于当函数结束后,函数栈帧会销毁,n的空间也会被释放,所以要有一个寄存器来保存数据.

下面的代码就可以表现出来:

int main()
{
	int& ret = func();
	return 0;
}

引用做返回值

引用做返回值会有很大的要求,这个和我们普通的返回值可不一样.说实话,我不想和大家分享的那么深,但是已经到这里了,只能这样了.

下面的代码,我们可以理解,就是给静态变量n取一个别名,我们把它返回到了ret.

int& func()
{
	static int n = 1;
	n++;
	return n;
}
int main()
{
	int ret = func();
	cout << ret << endl;
	return 0;
}

也就是说我们n取一个别名,把这个别名作为返回值返回给函数调用者.

int& func()
{
	static int n = 1;
	n++;
	cout << &n << endl;
	return n;
}

int main()
{
	int& ret = func();     //  注意 是 int&
	cout << &ret << endl;
	return 0;
}

注意事项:

到这里我们就可以看看引用做返回值的注意事项了,我们一定要保证做返回值得引用得数据在函数栈帧销毁后空间不被释放,否则就会发生下面得事情.

我们得到的是一个随机值,我们拿到了变量 n 的别名,但是在func结束后空间就被释放了,下一次函数的调用函数栈帧会覆这篇空间,运气好的话,我们有可能拿到准确值,但是无论如何访问都越界了.

int& func()
{
	int n = 1;
	n++;
	return n;
}

int main()
{
	int& ret = func();
	printf("这是一条华丽的分割线\n");
	cout << ret << endl;
	cout << ret << endl;
	return 0;
}

引用不会开辟空间

前面我们说了传参需要有一定的要求,但是这不是说引用做参数不行,我们使用引用传参不会发生拷贝,这极大的提高了代码的效率.

我们定义一个大点的结构体,来看看拷贝传参和引用传参的效率.一般情况下相差大概20倍左右.

typedef struct A 
{
	int arr[10000];
} A;

void func1(A a)
{

}

void func2(A& a)
{

}

void TestRefAndValue()
{
	A a;

	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 100000; ++i)
		func1(a);
	size_t end1 = clock();

	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; ++i)
		func2(a);
	size_t end2 = clock();

	// 分别计算两个函数运行结束后的时间
	cout << "func1(A)-time:" << end1 - begin1 << endl;
	cout << "func2(A&)-time:" << end2 - begin2 << endl;
}

int main()
{
	TestRefAndValue();
	return 0;
}

引用和指针比较

它们有一个本质的区别,在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间 ,但是引用的底层还是开辟空间的,因为引用是按照指针方式来实现.我们看看汇编代码.

  • 引用不会开辟空间,但是指针会开辟相应的空间.

底层引用和指针是一样的.

int main()
{
	int a = 10;
	//语法没有开辟空间  底层开辟了
	int& b = a;
	b = 20;

	//语法开辟空间  底层也开辟了
	int* pa = &a;
	*pa = 20;
	return 0;

我们来看看它们其他的比较小的区别,我就不详细举例了.

  • 引用在定义时必须初始化,指针没有要求
  • 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  • 没有NULL引用,但有NULL指针
  • sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  • 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  • 有多级指针,但是没有多级引用
  • 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  • 引用比指针使用起来相对更安全

内联函数

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率,本质来说就和我们的宏比较相似.

记住我们不关心他们在那个过程中展开,只需要记住他会展开就可以了.我么看看内联函数的知识点.

为何存在 内联函数

对于一些比较小的函数,我们总是调用函数开销比较的,我们是不是存在一个方法减少这个开销呢?C语言通过宏来实现,C++支持C语言,所以我们可以通过行来实现,那么你给我写一个两数现相加的宏,要是你写的和下面的不一样,就代表你忘记了一部分知识点.

#define ADD(x,y) ((x) + (y))       //不带  ;   括号要带

我们就可以理解了,宏很好,但是写出一个正确的宏很困难,但是写一个函数就不一样了,所以一些大佬就发明了内敛函数,用来替代宏的部分功能.

展开短小的函数

函数内联不内联不是你说了算,我们用inline修饰就是告诉编译器这个函数可以展开,至于是否展开还是看编译器,一般之后展开比较短小的函数,较大的函数不会展开,像递归的那些也不可以.

inline void swap(int& x, int& y)
{
	int ret = x;
	x = y;
	y = ret;
}

int main()
{
	int a = 1;
	int b = 2;
	swap(a,b);
	cout << "a: " << a << endl;
	cout << "b: " << b << endl;
	return 0;
}

我们来看看内联函数,如果函数不是内联了的,汇编语言会call

void swap(int& x, int& y)
{
	int ret = x;
	x = y;
	y = ret;
}

如果在上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用.

由于我们使用的是VS编译器,这里需要看看内联函数的汇编语言.

在release模式下,查看编译器生成的汇编代码中是否存在call swap,但是编译器会发生优化,我们通过debug模式下,但是需要设置.

inline修饰的较短函数展开了,没有call

inline void swap(int& x, int& y)
{
	int ret = x;
	x = y;
	y = ret;
}

内联函数的特性

我们需要来看看函数的基本的特性

  • inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环 / 递归的函数不适宜使用作为内联函数。
  • inline 对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等,编译器优化时会忽略内联。
  • inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

较大的函数编译器不会发生内联

编译器会自动判别这个函数给不该内联,要是一个函数比较大,里面存在递归,那么还不如不展开呢.一般是10行为依据.

inline void f()
{
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
	cout << "hello" << endl;
}
int main()
{
	f();
	return 0;
}

声明定义一起

如果我们声明和定义的分离,运行时编译器会找不到这个函数的地址的.

这里我么建议直接把内联函数直接放到自定义的头文件中

inline void f()
{
}

到此这篇关于C++ 引用与内联函数详情的文章就介绍到这了,更多相关C++ 引用内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++ 引用与内联函数详情

本文链接: https://www.lsjlt.com/news/148693.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++ 引用与内联函数详情
    目录引用初阶什么是引用为何要有引用引用指向同一块空间引用的特性定义时必须初识化一个变量可以多次引用引用一旦引用了一个实例,不能在再引用其他的实例引用进阶常引用权限临时变量具有常属性引...
    99+
    2022-11-13
  • C++内联函数详情
    内联函数是C++当中为了提高程序运行效率的设计,老实讲我没有在其他语言当中看到类似的设计。它和常规函数之间的主要区别不在于编写的方式,而是在于C++编译器会将内联函数组合到程序当中...
    99+
    2022-11-12
  • C++引用与内联函数怎么使用
    这篇文章主要介绍“C++引用与内联函数怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“C++引用与内联函数怎么使用”文章能帮助大家解决问题。一、引用首先我们来看一下引用的概念:1.1引用的概念...
    99+
    2023-07-05
  • C++ 引用与内联函数实例分析
    这篇文章主要介绍“C++ 引用与内联函数实例分析”,在日常操作中,相信很多人在C++ 引用与内联函数实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++ 引用与内联函数实...
    99+
    2023-06-30
  • c++内联函数怎么引用
    在C++中,内联函数是通过将函数的定义放在每个调用点处进行替换,而不是通过函数调用来实现的。因此,在引用内联函数时,需要确保函数的定...
    99+
    2023-10-26
    C++
  • C++ 内联函数详解
    目录一、C++ 内联函数1.内联2.语法3.评价总结一、C++ 内联函数 1.内联 C 语言中有宏函数的概念。宏函数的特点是内嵌到调用代码中去,避免了函数调用的开销。但是由于宏函数的...
    99+
    2022-11-12
  • C++类与对象深入之引用与内联函数与auto关键字及for循环详解
    目录一:引用1.1:概念1.2:引用特性1.3:常引用1.4:使用场景1.5:引用和指针的区别二:内联函数2.1:概念2.2:特性2.3:面试题三:auto关键字3.1:auto简介...
    99+
    2022-11-13
  • C++ 内联函数inline案例详解
    使用函数能够避免将相同代码重写多次的麻烦,还能减少可执行程序的体积,但也会带来程序运行时间上的开销。 函数调用在执行时,首先要在栈中为形参和局部变量分配存储空间,然后还要将实参的值复...
    99+
    2022-11-12
  • C++入门教程之内联函数与extern "C"详解
    目录一.   内联函数1.概念及分析2.特性3.宏二.   extern “C”1.C++程序2.C程序 总...
    99+
    2023-01-10
    c++内联函数是什么 c++  extern c c++内联函数使用
  • C++内联函数不宜使用情况的示例分析
    这篇文章将为大家详细讲解有关C++内联函数不宜使用情况的示例分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。C++语言中的各种应用是一个非常高深的内容,其中的语法、特性、标准类库都是我们值...
    99+
    2023-06-17
  • C++中引用、内联函数、auto关键字和范围for循环详解
    目录一、前言二、引用三、内联函数四、auto关键字五、范围for循环六、结语一、前言 大家好久不见,这篇文章带大家学习一下C++中的引用和内联函数 二、引用 引用变量是一个别名,也就...
    99+
    2023-02-10
    c++ auto关键字 c++中的内联函数 c++引用的作用是什么
  • C++超详细分析讲解内联函数
    目录宏函数(带参数的宏)的缺点inline修饰的函数就是内联函数内联函数的特点宏函数和内联函数的区别宏函数(带参数的宏)的缺点 第一个问题:宏函数看起来像一个函数调用,但是会有隐藏一...
    99+
    2022-11-13
  • C#中内联函数怎么用
    这篇文章将为大家详细讲解有关C#中内联函数怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。C++ 中有个内联函数,使用 inline 来修饰函数,编译器就会对其进行优化,将此函数作为代码判断插入到调用...
    99+
    2023-06-29
  • C++宏函数和内联函数的使用
    目录1. 宏常量&宏函数1.1 定义1.2 使用宏函数的注意事项2. 内联函数2.1 定义2.2 某些情况编译器不会做内联编译3. 补充3.1 宏定义和函数的区别3.2 宏定...
    99+
    2022-11-13
  • C++中的友元函数与友元类详情
    目录一、问题背景二、友元函数三、友元类一、问题背景 对类的封装是C++三大特性中的一个重要特性,封装好的数据在类的外部是访问不到的但是一旦出了问题,想要操作被封装的数据怎么办呢?由此...
    99+
    2022-11-13
  • C++内联函数inline与auto关键字怎么使用
    这篇文章主要介绍“C++内联函数inline与auto关键字怎么使用”,在日常操作中,相信很多人在C++内联函数inline与auto关键字怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++内联函数...
    99+
    2023-06-30
  • C++宏函数和内联函数如何使用
    今天小编给大家分享一下C++宏函数和内联函数如何使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1. 宏常量&宏函...
    99+
    2023-07-02
  • C++浅析内联函数的使用
    目录一. 概念二. 特性一. 概念 以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销, 内联函数提升程序运行的效率。 在C++里...
    99+
    2022-11-13
  • C++内联函数有什么作用
    这篇文章主要介绍“C++内联函数有什么作用”,在日常操作中,相信很多人在C++内联函数有什么作用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++内联函数有什么作用”的疑惑有所帮助!接下来,请跟着小编一起来...
    99+
    2023-06-17
  • C++的内联函数怎么使用
    本文小编为大家详细介绍“C++的内联函数怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++的内联函数怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、常量与宏回顾C++中的const常量可以替...
    99+
    2023-06-30
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作