前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【细品C++】引用

【细品C++】引用

作者头像
Crrrush
发布2023-06-23 14:37:38
1740
发布2023-06-23 14:37:38
举报
文章被收录于专栏:后端开发练级指南

写在前面

本篇文章将带你了解C++引用。引用作为C++新设计的类型,其功能与指针有所交集,在一定程度上代替了一些指针的用法,而希望本篇文章能令你引用的理解有帮助。

引用概念

根据C++Primer上的解释,引用是一种复合类型,通过在变量名前添加“&”符号来定义。复合类型是指用其他类型定义的类型。在引用的情况下,每一种引用类型都“关联到”某一其他类型。不能定义引用类型的引用,但可以定义任何其他类型的引用。

通俗地讲,引用不是定义一个新的变量,而是给已存在变量取一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。此外,引用类型与实体类型必须是同种类型

引用代码演示:

代码语言:javascript
复制
void demo1()
{
	int a = 10;
	int& ra = a;//定义引用类型变量

	printf("%d\n", a);
	printf("%d\n", ra);//使用上当作变量a使用
}

引用特性

  1. 引用在定义时必须初始化。
  2. 一个变量可以有多个引用。
  3. 引用初始化后,不能再绑定到其他实体(对象)。
代码语言:javascript
复制
void demo2()
{
	int a = 3;
	int b = 5;

	//int& ra; //这句代码编译会出错
	//error C2530: “ra”: 必须初始化引用

	int& ra1 = a;
	int& ra2 = a;
	int& ra3 = ra1;

	ra1 = b;//这句只是赋值
	//两个不相同的地址
	cout << &ra2 << endl;
	cout << &b << endl;
	cout << ra1 << endl;
	cout << ra2++ << endl;
	cout << ra3++ << endl;
	cout << a << endl;
}

常引用

常引用,或者说const引用,是指向const对象的引用。注意,const本质是赋予修饰的变量(对象)常属性,也就是说const引用是对具有常属性的实体的引用。

代码演示:

代码语言:javascript
复制
void demo3()
{
	const int a = 3;
	//int& ra = a;// a是具有常属性的变量
	// error C2440: “初始化”: 无法从“const int”转换为“int &”
	const int& ra = a;

	//int& rb = 10;// 10是常量
	// error C2440: “初始化”: 无法从“int”转换为“int &”
	const int& rb = 10;
	
	double c = 3.1415;
	//int& rc = c;// 引用类型与实体类型不相同
	// error C2440: “初始化”: 无法从“double”转换为“int &”
	const int& rc = c;
	//本质上rc引用的是强制类型转换时产生的临时变量,
	//此临时变量是不能被修改的,具有常属性
}

使用场景

  1. 做参数
代码语言:javascript
复制
  int& Count1()//1 
  {
  	static int n = 0;
  	//...
  
  	return n;
  }

做返回值

代码语言:javascript
复制
int& Count2()//2 wrong
{
	int n = 0;
	//...
	return n;
}

//想想为什么Count2的写法是错误的
//
// 对于Count1来说,由static修饰的n的数据存放于静态区,出了函数生命周期时并不会释放该空间
// 
// 对于Count2来说,变量n的数据只是存在与Count2函数栈帧中,
// 出了函数生命周期时,这一整块函数栈帧都会被释放
//
// 这就意味着这块空间随时都有可能被访问或者修改
//
// 所以,使用int&作为返回值时,对于返回的数据必须不是出了函数栈帧2就销毁的数据
// 例如全局变量,static修饰的数据,malloc申请出来的数据
//

传值、传引用效率比较

以上的事情,对于C语言来说,使用指针也能实现,当然,引用相较于指针的使用必然是比较便利的,那么C++设计出的引用相比于指针仅仅就只有这一点优势吗?答案自然不是,毕竟语言设计者们可都是真正的大佬呀。

首先,以值作为参数或者返回值类型,在传参和返回期间,函数并不会直接传递实参或者将变量本身直接返回,而是在传递实参或返回变量的一份临时拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率会更加低下。

代码语言:javascript
复制
#include<time.h>
typedef struct A
{
	int n[100000];
}A;

A a;

A test1()
{
	return a;
}

A& test2()
{
	return a;
}

void demo5()
{
	//直接以值为返回值类型
	size_t begin1 = clock();
	for (int i = 0; i < 10000; i++)
		test1();
	size_t end1 = clock();

	//以引用为返回值类型
	size_t begin2 = clock();
	for (int i = 0; i < 10000; i++)
		test2();
	size_t end2 = clock();

	cout << "test1 time: " << end1 - begin1 << "ms" << endl;
	cout << "test2 time: " << end2 - begin2 << "ms" << endl;
}

引用和指针的区别

不同于指针,在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。

但引用在底层实现上实际是有空间的,因为引用是按照指针方式来实现的

在以下引用和指针操作的实现中,转到反汇编看汇编指令时,你会发现使用的汇编指令是一样的。

(但是实际使用中并不需要你去关心这一点)

代码语言:javascript
复制
void demo6()
{
	int a = 1;

	int& ra = a;
	ra = 2;

	int* pa = &a;
	*pa = 1;


}

总结:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求。
  3. 引用在初始化时引用一个实体后,就再也不能引用其他实体(事实上,从语层面上就无法做到,没有能改变引用实体的语句),而指针可以在任何时候指向任何一个同类型实体。
  4. 没有NULL引用,但有NULL指针。
  5. sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)。
  6. 引用自加即引用的实体也自加,指针字节即指针指向从原指向空间向后偏移一个类型大小的地址空间。
  7. 多级指针没多级引用
  8. 访问实体方式不同,指针需要显式解引用引用编译器自己处理
  9. 引用比指针使用起来相对较安全

结语

以上就是C++的引用详解,引用作为C++新设计的类型,最初的设计意图也是为了解决C指针使用的一些不便和痛点,所以引用类型还是非常好用的。最后,非常感谢读者朋友们能够读完本篇文章。不知这种短小的文章读起来体验如何,我在尽可能精简文章,让读者能按需索取,不浪费时间。如果你觉得做的还不错的话请点赞收藏加分享,当然如果发现我写的有错误或者有建议给我的话欢迎在评论区或者私信告诉我。

彩蛋

GitHub源码 (文件名:引用) gitee源码 (文件名:引用)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-09-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
  • 引用概念
  • 引用特性
  • 常引用
  • 使用场景
  • 传值、传引用效率比较
  • 引用和指针的区别
  • 结语
  • 彩蛋
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档