C++引用

上一篇:C++指针

1. 概述

上篇我们讨论了指针,这篇我们来了解了解C++中的引用。如果你还没有看上篇的指针,强烈推荐你先看一下,因为引用实际上只是指针的扩展。所以你需要了解,至少在基本层面上,了解指针是如何工作的,才能更好的理解这个篇章。

指针引用在C++甚至其他语言中被大量提及的两个关键字。本篇中说的指针和引用,计算机现在用它们做的事情几乎是一样的。从语义上讲,我们如何使用和书写他们有一些细微的区别。但根本上,引用通常只是指针的伪装,引用只是在指针上的语法糖,让指针更容易阅读和理解。引用基本上就和听起来一样,是一种我们引用现有变量的方式。不像指针,你可以创建一个新的指针变量,然后设置他等于空指针或其他类型的指针,指针可以设置为0。但引用不行,因为引用必须"引用"已经存在的变量,引用本身并不是新的变量,它们不占用内存,它们没有真正的内存空间,它们不像典型的变量,因为它们是作为一个变量的引用。

2. 案例

下面开始案例

1. 准备项目

1. 引用

我们有一个简单的项目,项目中有一个main.cpp文件,文件内容如下

现在,我们创建一个int型的变量a,并给它赋值为5。如下

变量a是一个整数,如果我想给这个变量创建一个引用,我可以输入变量的类型然后后面紧跟着&符号,如int&

2. '类型+&' 与 '&+变量' 的区别

arduino 复制代码
'类型+&':引用声明
'&+变量':取变量内存地址

注意这个&符号实际上是变量声明的一部分。在上篇C++指针中,我们了解,如果我们创建一个指针,我们可以使用一个&运算符来实际获取现有变量的内存地址,如int* ptr = &a;&a是获取变量a的内存地址。在int&这里不同,因为这里的&符号实际上是类型的一部分,它实际上不在现有变量的旁边。int&中的&号是类型的一部分,所以要注意这个区别。因为有很多人只要看到&号,他们就认为都是引用亦或者认为都是取地址。所以&号具体是什么取决于上下文。像这样int&&在类型的旁边,所以它是一个引用。

好,我们继续,我们给这个引用取名ref,并赋值为a,int& ref = a;

就是这么简单,我们只是让引用ref等于一个现有变量a。

所以我们现在所做的就是创造了一个叫做别名的东西,因为这个ref,它并不是一个真正的变量,只是一个引用,ref"变量"实际上并不存在,它只存在于我们的源代码中。如果我们现在编译我们的代码,我们不会得到两个变量a和ref,我们只会得到变量a。

我们现在能做的是,可以使用ref就像是使用a一样,如果我们设ref等于2,然后我们来打印a,如下

F5运行程序

可以看到打印a的值是2,我们通过给ref赋值等于2,改变了a的值。因为不管出于什么目的,ref都是a,我们给a记了个别名,在这种情况下,我们的引用不是一个指针或类似的东西,编译器不需要实际创建一个新变量。如果你编译代码,这个代码ref = 2;就相当于你设置了a=2;。这只是我们可以在源代码中编写,以使我们的生活更简单的东西,如果我们现在想给一个变量起别名的话。

让我们尝试更复杂的东西。

3. 值传递、指针传递和引用传递

1. 值传递

假设我想写一个函数,void Increment(int value),函数体中递增value的值。如果我们这样写这个函数,接收到参数是一个值。然后修改main函数代码,我们在main函数中调用Increment函数,将a作为参数传递进去,只是通过值传递它,看看会发生什么

在函数Increment的接收参数int value中,可以看到,我们并没有把接收的参数把它作为一个指针int* value或者一个引用int& value或者类似的东西传递。

这里会拷贝a变量的值5,复制到函数Increment中,复制会创建一个全新的变量,就像这样写一样int value = 5;

这就是将要发生的事情。我可以证明上面这点,恢复Increment函数。

F5运行程序

可以看到,控制台实际打印的是5,就是变量a的初始值。没有改变。因为Increment函数是按值传递,并没有真正的修改变量a的值,而是在函数Increment自己创建了一个变量来递增。

2. 指针传递

我需要做的是通过引用来传递变量,这样变量a才会递增。因为我们真正想做的是影响这个a变量。那么我们应该怎么做呢?如何通过将这个变量传递到函数中来修改它呢?

上次我们讨论了指针,还记得指针就是我们的内存地址吧,所以从理论上讲,我们不把将实际值5传递给函数,而是可以把变量a的内存地址传递过去。因为我们可以在这个函数中做的是,我们可以查找那个内存地址,看到值5,然后修改那个内存地址,我们可以写入该内存地址,因为我们已经将该内存地址传递给了函数。

下面来看下使用指针的方式

首先我们修改Increment函数的参数,修改为int* value就收一个指针。

在main函数中,调用Increment函数传入的是变量a的内存地址,而不仅仅是值5,可以用&a取到变量a的内存地址Increment(&a);

接下来,就是逆向引用*Increment函数接收的指针value,*value,这样我们就可以直接写入内存。

这里是先逆向引用*指针value,而不是直接对指针value自增。如果不加逆向引用*,那就是直接对地址进行递增,然而,指针只是一个整数,如果我们不再value前面加一个*号,没有dereference操作符*,那么内存地址就会递增,而不是内存中实际的值。(*value)++;这里加括号,因为不加括号,因为操作的顺序,它会先做value++增量运算,然后再做逆向引用,和我们想要的相反。我们想要的是先逆向引用获取到值然后再递增。

F5运行程序

可以看到打印了6。现在符合了我们的预期。我们成功通过指针将变量传递到一个函数中。

3. 引用传递

然后本篇是关于引用的。如果使用引用,我们可以做的比刚刚更加简单,用更少代码和更少的修饰语法。

所以我们能做的就是重写这个Increment,用引用int& value来代替指针int* value,这意味着我们不需要用到逆向引用了。在main函数中,我们不需要传递a的内存地址,只需要传递a即可

由于它是通过引用传递的,我们基本上重写了代码,但做了完全相同的事情。当它编译时,它和我们之前写的完全一样,然而,这一次,我们的代码看起来更漂亮,这也是唯一的区别了。

我们F5运行代码

这就是引用的全部真相了。它们只是语法糖,对于引用,没有什么是指针不能做的。指针就像引用,但指针更有用,更强大。然而如果你可以使用引用,就像上面例子那样,那么就一定要使用引用,因为引用会让代码更加简洁和简单。而不是像指针,那么麻烦,而引用使得你的代码干净得多。

4. 引用一旦声明赋值,就无法引用其他变量

关于引用,我想提到的另一件重要的事情是,一旦你声明了一个引用,你不能改变它引用的东西。假设我有两个整数a和b,a设为5,b设为8,int a = 5; int b = 8;,然后我要做的是声明一个引用,被设为a的引用 int& ref = a;,然后可能稍后在我的代码中,用我们的引用ref去引用b,我们可以这样做吗?

答案是否定的,我们不能这样做。在这个例子中,我们创建了一个对变量a的引用ref,当我们决定把这个引用ref设为b时,意思是a的值被赋值为b的值,也就是8,也就是ref = b;,执行完后,a等于8,b等于8。这就是我们会得到的。

F5运行程序

可以看到,如果我们给变量a的引用ref赋值变量b,这会导致变量a变为8,然后Increment(a);函数变量a自增1,所以控制台输出9。也就是说,我们并没有改变引用实际引用的变量,ref一直引用的都是a。

这也意味着当我们声明一个引用时,我们需要把它进行赋值,且不能不赋值

如果不赋值,编译器会给出提示,引用需要一个初始化声明,当我们声明一个引用时,我们必须马上给它赋值。因为引用必须引用一些东西,记住引用不是一个真正的变量,只是一个引用。

在这个例子中,如果我真的需要实现更改引用的功能,我们该怎么做?

5. 指针变量可更改指向内存地址

如果我真的想改变ref引用的东西,正如我之前说的,引用不是一个真的变量,所以我们需要创建变量。我们可以首先设置这个变量来指向a,然后,我们更改为指向b,注意,我在这里说的是指向,所以这里指的是指针。

所以我们来改下代码

我们把int& ref引用改为int* ref指针,我们一开始可以把指针ref设置为变量的内存地址(&a),也就是int* ref = &a;。当我想把指针变量ref的指向变成b时,ref = &b;,指针变量ref指向了b。

还记得吧,要想改变指针指向的值,我们需要先逆向引用 *这个指针*ref,然后给它赋值。在这个例子中,我们让a等于2,让b等于1,然后打印变量a和变量b,如下

F5运行程序

可以看到打印出来的是2和1。

上一篇:C++指针

相关推荐
家有狸花1 小时前
VSCODE驯服日记(三):配置C++环境
c++·ide·vscode
dengqingrui1232 小时前
【树形DP】AT_dp_p Independent Set 题解
c++·学习·算法·深度优先·图论·dp
C++忠实粉丝2 小时前
前缀和(8)_矩阵区域和
数据结构·c++·线性代数·算法·矩阵
ZZZ_O^O2 小时前
二分查找算法——寻找旋转排序数组中的最小值&点名
数据结构·c++·学习·算法·二叉树
小飞猪Jay5 小时前
C++面试速通宝典——13
jvm·c++·面试
rjszcb6 小时前
一文说完c++全部基础知识,IO流(二)
c++
小字节,大梦想6 小时前
【C++】二叉搜索树
数据结构·c++
吾名招财6 小时前
yolov5-7.0模型DNN加载函数及参数详解(重要)
c++·人工智能·yolo·dnn
我是哈哈hh7 小时前
专题十_穷举vs暴搜vs深搜vs回溯vs剪枝_二叉树的深度优先搜索_算法专题详细总结
服务器·数据结构·c++·算法·机器学习·深度优先·剪枝
憧憬成为原神糕手7 小时前
c++_ 多态
开发语言·c++