【详解C++中的引用】

这篇具有很好参考价值的文章主要介绍了【详解C++中的引用】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


一、什么是引用

引用就是给一个变量取别名。

注意:这个引用不会新开辟一块空间,而是和原来的变量公用一块空间。

举个例子:李逵,在家称为"铁牛",江湖上人称"黑旋风"。
【详解C++中的引用】

二、引用规则

引用规则:引用实体类型+&+引用别名 = 引用实体。

比如下面:

int main()
{
	int a = 10;
	//引用
	int& ba = a;
	
	ba = 20;
	printf("%d ", a);

	return 0;
}

上面代码为例:

引用对象类型是int + & + 引用别名(ba) = 引用对象(a)

C++中的 “&”符号跟类型在一起是不在是取地址,而是”引用“。

现在ba这个就是a的别名,和a是同一块内存空间,改变了ba的内容,就等于改变了a的内容。

同时,一个变量可以有多个引用。

相当于一个人可以有多个别名一样。

就像是:假如你在家被叫做小红,在外面被叫燕燕。你的妈妈叫小红吃饭,然后小红去吃饭了,那燕燕是不是也吃了,你是不是也吃了呢?

三、引用特性

1.引用类型必须是和引用实体是同一类型。

比如:
【详解C++中的引用】
报错的原因是:引用对象的类型和引用实体的类型不一致。

引用的对象a是int类型,而给它取别名却是double类型,这是不允许的。

2.引用在定义时必须初始化

比如:

【详解C++中的引用】
这也是不允许的。

3. 引用一旦引用一个实体,就不能引用其他实体。

比如:

int main()
{
	int a = 10;
	//引用
	int& pa = a;

	int x = 20;
	pa = x;

	return 0;
}

这段代码,有错误吗?

没有错误,但是pa = x这行代码,并不是引用更改,因为前面已经提过:

引用一旦引用一个实体,就不能引用其他实体。

所以这行代码的意思是:将x 的值赋值给pa。

此时a的值是20了。

四、使用场景
1.做函数参数

引用可以做函数的参数,作用相当于指针,可以改变变量本身。

比如:

void Swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

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;
}

传参传一个实参过去,但是接收时使用引用,此时该引用就是形参的别名,形参的别名改变会改变形参本身。

这个是使用引用一个好的地方。

做返回值的作用:
1.做输出型参数,节省空间。
我们传形参时,用引用接收,也不再需要使用地址,不再需要开辟指针空间来接收,直接改变引用即可改变形参。

2. 提高效率,当传过来的形参是大对象/深拷贝对象时,能够极大地提高效率。

2.做返回值

先看不用引用做返回值,用普通的返回值:


案例1,错误代码
int test()
{
	int n = 10;
	n++;

	return n;
}

int main()
{
	int ret = test();

	printf("%d\n", ret);
	return 0;
}

这样的返回值是我们常见的返回值。
这里需要提一点:
test函数并不是直接返回n的。

因为test函数在调用结束后会销毁它所在的栈帧,连同n会一起销毁,所以编译器会先保存n的值到一个寄存器中,再销毁栈帧,然后返回寄存器的值给ret。

过程如下:
【详解C++中的引用】
所以就出现了一个问题。

当我们用上面的代码,返回的是n的引用(别名)时,这就不安全了。因为返回的是n的引用,不会创建临时空间给n,而是直接返回n。 但是返回之后n所在的函数栈帧会被销毁,所以连同n一起销毁了,但是此时ret是n这块已经不属于自己的空间的拷贝,所以ret是违法的。

打印出来的ret,可能是随机值,也可能是n原来的值,但如果是n原来的值,这只是侥幸,因为n原来的空间暂时没有被使用。但如果n这块空间被其他函数使用了,此时ret就有可能是随机值。

所以在上面的例子中,不能使用引用来返回。

再看下面的例子:

案例2 ,正确代码
int& test()
{
	static int n = 10;
	n++;
	return n;
}

int main()
{
	int ret = test();
	printf("%d\n", ret);
	return 0;
}

注意,n是被static修饰过的变量。此时可以用引用进行返回了,因为函数test的销毁不会销毁n,n是在静态区开辟的空间,而函数是在栈区开辟的空间,两者互不影响。

返回n的引用是绝对安全的。

再看下面:

案例3,错误代码
int& test()
{
	int n = 10;
	n++;
	return n;
}

int main()
{
	int& ret = test();
	printf("%d\n", ret);
	return 0;
}

此时与案例二相似,但是n不是被static修饰过的,而且ret也是引用,相当于返回n的引用后,再用引用接收n的引用,此时ret也还是n的别名,而n是在栈区开辟的空间,销毁后,此时n的空间不再属于自己,打印ret,相当于打印不属于自己的n,这是违法的行为。

当这段栈空间被其他东西用之后,n的值可能是随机值了。
【详解C++中的引用】

再看下面的案例:

案例4,正确代码
int& test()
{
	static int n = 10;
	n++;
	return n;
}

int main()
{
	int& ret = test();
	printf("%d\n", ret);
	return 0;
}

此时n 是被static修饰过的,所以栈帧空间的销毁不会影响n,打印ret(ret是n的引用),会正确打印出来。

总结:
1.使用引用做返回值时,引用返回不会开辟临时空间保存返回值
2.而不管改变量是在栈区还是在静态区,不用引用都会开辟临时空间保存返回值。
但是使用引用必须保证返回值是绝对的安全。

五、常引用

1.在引用的过程中,权限不能放大,只能缩小或平移。
1.在引用的过程中,权限不能放大,只能缩小或平移。
1.在引用的过程中,权限不能放大,只能缩小或平移。

案例1:
错误示例

int main()
{
	//权限不能放大,不正确
	const int a = 0; // 常变量,不可修改
	int& b = a; // 起了一个别名,必须也是常引用
	
	return 0;
}

a是一个被const修饰后的常变量,不可修改。
而b是a的引用,此时b并没有被const修饰,意味着b可以修改,但是a已经不能被修改了,b是a的别名同样不可修改,这是规定。

所以权限不能放大

案例2:
正确实例

int main()
{
	const int a = 0; // 常变量,不可修改
	//权限的平移
	const int& b = a;
	return 0;
}

变量a 和引用b都被const修饰了,它们的权限是一样的,所以权限可以平移。

案例3:
正确案例:

int main()
{
	//权限可以缩小
	int g = 0;
	const int& h = g;
	return 0;
}

g是一个变量,h是g的引用,但是h被const修饰后,意味着h不能修改。
所以这是一个权限的缩小。

2.只要发生类型转换,都会产生临时变量。
而临时变量不可修改,具有常性。

2.只要发生类型转换,都会产生临时变量。
而临时变量不可修改,具有常性。

2.只要发生类型转换,都会产生临时变量。
而临时变量不可修改,具有常性。

类型转换包括:隐式类型转换,截断,整型提升等。

比如:

int main()
{
	double d = 1.1;
	int a = d;

	return 0;
}

这里会发生隐式类型转换。d是double类型拷贝给a ,会在中间生成一个int类型的临时变量,然后把d放入该临时变量中,然后该临时变量再拷贝给a。所以把d拷贝给a是不会改变d本身的。
【详解C++中的引用】
再看下面的代码:

错误示例
int main()
{
	double d = 1.1;
	int& a = d;
	return 0;
}

为什么a不能作为d的引用?
本质上还是权限放大的问题。
d是double类型的变量,a是int类型的引用,中间创建一个int类型的临时拷贝,而临时拷贝具有常性,给了a之后,a是引用,不具有常性,就相当于权限的放大。

正确示范
int main()
{
	double d = 1.1;
	const int& a = d;
	return 0;
}

加了const修饰后,引用a就具有了常性,相当于权限的平移了。

ps:为什么类型转换会产生临时变量?

看下面的例子:

int main()
{
	int a = 1;
	double b = 1.1;
	if (b > a)
	{
		cout << "hehe" << endl;
	}
	return 0;
}

此时会打印hehe,因为表达式左右两边如果类型不同,会发生整型提升或截断。

这里b和a相比,a会发生整型提升到double类型再与b比较。

而整型提升的过程会生成一个临时变量,这个临时变量就是a提升后的结果(并不是a本身提升)。

所以比较前后a和b的值都不会发生改变。

六、引用和指针的区别

从汇编的角度看,引用的底层也是用指针实现的。
【详解C++中的引用】

但是在语法层面,引用不开空间,指针会开辟一块空间。

总结

引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
int main()
{
	int a = 10;
	//指针存储a的地址
	int* pa = &a;
	
	//b是a的引用(别名)
	int& b = a;
	return 0;
}
  1. 引用在定义时必须初始化,指针没有要求。
int main()
{
	int a = 1;

	//引用必须初始化
	int& b = a;

	//指针可不初始化
	int* p;
	return 0;
}
  1. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体。
int main()
{
	int a = 10;
	int b = 20;
	int& d = a;

	//指针可以改变指向的对象
	int* p = &a;
	p = &b;

	//但是引用不可改变指向的对象
	//这个并不是改变引用d的实体对象,而是把b拷贝给d
	d = b;
	return 0;
}
  1. 没有NULL引用,但有NULL指针。
int main()
{
	//可以存在空指针
	int* p = NULL;
	//不存在空引用,引用必须初始化一个实体
	//错误代码
	int& b;
	return 0;
}
  1. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)。
int main()
{
	double a = 10;
	
	double* p = &a;
	cout << sizeof(p) << endl;

	double& b = a;
	cout << sizeof(b) << endl;
	return 0;
}

【详解C++中的引用】

  1. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。
    【详解C++中的引用】

  2. 有多级指针,但是没有多级引用。

  3. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理。

  4. 引用比指针使用起来相对更安全。文章来源地址https://www.toymoban.com/news/detail-421548.html

到了这里,关于【详解C++中的引用】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python 变量?对象?引用?赋值?一个例子解释清楚

    哈喽大家好,我是咸鱼。 前天有个小伙伴找到我,给了我一段 python 代码: 然后问我为什么结果是 [1, [...]] ,我一看这个问题有意思,我说三言两语解释不清楚,我写篇文章到时候你看下吧,于是有了今天这篇文章。 在正式开始之前,让我们先弄清楚一些概念。 \\\"Python 中一

    2024年01月24日
    浏览(45)
  • C++的引用变量作为函数参数

    在C++的中新增一个特性:按引用传递变量,虽然与按传递变量的地址可以实现相同的结果,但引用有其独特的地方。 引用传递: 引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。 通过引用变量用作参数,函数将使用原始数据,而不是其副本。 举

    2023年04月14日
    浏览(37)
  • C++笔记之设计模式全局状态管理类:使整个工程项目中的所有函数可以访问同一个变量,并且能够感知到这个变量的变化(变量共享)

    —— 杭州 2024-03-21 夜 实现方法 : 全局变量(不介绍) 单例模式 全局状态管理类 全局状态管理类+单例模式 静态变量或静态成员(不介绍) code review!

    2024年03月22日
    浏览(65)
  • maven pom中的内置变量及引用

    maven其实有很多内置变量供开发着在开发中使用,比如说basedir这变量,它指的是pom.xml文件所在的目录,下面我们一起来认识一下。 变量名 作用 说明 basedir 、project.basedir 项目的根目录 即包含 pom.xml 文件的目录 project.groupId 项目的 groupId project.artifactId 项目的 artifactId project.v

    2024年01月24日
    浏览(34)
  • 「速通Shell」嵌点入线,Shell变量引用详解

    上一篇我们了解了Shell变量的定义和分类,对不同变量类型的特点和作用有了初步认识,今天我们将讲述Shell编程中变量该如何引用,将其融入到shell语句中,并进行相关操作。 Shell变量引用是指在Shell脚本中,使用变量名来获取该变量存储的值的过程。在Shell脚本中,使用变量

    2024年02月03日
    浏览(51)
  • 【C++】C++ 引用详解 ⑦ ( 指针的引用 )

    指针的引用 效果 等同于 二级指针 , 因此这里先介绍 二级指针 ; 使用 二级指针 作为参数 , 可以实现如下功能 : 动态内存管理 : 借助二级指针 , 可以在函数中分配或释放内存 ; 如 : 创建一个动态数组或调整现有数组的大小 , 在函数中需要一个指向指针的指针作为参数 , 以便修

    2024年02月11日
    浏览(42)
  • C++入门:引用是什么

    目录 1.引用的概念 2.引用的特征 3.常引用 4.引用使用场景 5.传值,传引用效率比较 6.引用与指针的区别 引用 不是新定义一个变量,而是 给已存在变量取了一个别名 ,编译器不会为引用变量开辟内存空 间,它和它引用的变量 共用同一块内存空间 。 语法: 类型 引用变量名

    2024年02月11日
    浏览(41)
  • 【C++】C++ 引用详解 ⑧ ( 普通引用与常量引用 | 常量引用概念与语法 )

    之前的 【C++】C++ 引用详解 ① ~ ⑦ 博客中 , 讲解的都是 普通引用 , 也就是 将 普通变量 赋值给 引用 , 过程如下 : 先定义 普通变量 a , 然后定义 已存在变量 a 的引用 b ; 这里的 引用 b 就是 普通引用 ; 普通引用代码示例 : 执行结果 : 与 变量引用 相对的就是 常量引用 ; 通过 普

    2024年02月11日
    浏览(36)
  • C++核心编程:C++中的引用

    作用:给变量起别名 语法: 数据类型 别名 = 原名 引用必须初始化 引用在初始化后,就不可以改变了 示例: 作用 :函数传参时,可以利用引用的技术让形参修饰实参 优点 :可以简化指针修改实参 示例: 作用:引用时可以作为函数的返回值存在的。 注意: 不要返回局部变量

    2024年02月13日
    浏览(32)
  • C++中的引用

    一、引用的概念  引用不是新定义一个变量,而是给已有变量取一个别名,编译器不会为引用变量开辟内存空间,而和它引用的变量共用一块内存空间。 注意,由于C++兼容C,所以既可以是引用符号,也可以是取地址 补充: 1.同一个变量可以有多个别名 2.可以对变量的别名再

    2024年01月25日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包