栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

一次性搞定c++的引用|建议收藏

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

一次性搞定c++的引用|建议收藏

就学习C++语言来说,引用是必然要理解的内容,今天这波文章就给大家肝一波引用的深入理解与使用,帮助大家理解应用,以便更好的学习C++语言

引用的概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它 引用的变量共用同一块内存空间,俗称:取别名

区别C语言,& 符号在C++当中是引用符号,不是取地址符号

一. 先看一个简单的引用程序

int main()
{
	int a = 10;
	int& b = a;//此时b和a指向同一块空间
	int& c = b;//再次为b取别名,此时 a b c 的地址是同一块空间
	return 0;
}

 二.引用的特征

1. 引用必须在定义的时候初始化

int& d;//此时错误,需要进行初始化

2. 一个变量可以有多个引用

int a = 10;
int& b = a;
int& c = b;//a可以取别名叫b,b又可以取别名叫c...

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

int e = 20;
b = e;//b在上面引用了a,就不能在引用e,此时是赋值操作

三. 深入理解常引用

先看一个代码,思考一下为什么会报错呢?

int main()
{
   double d = 5.5;
   int& f = d;//此时报错
   return 0;
}

我们知道,数据类型之间是存在隐式类型转换的,存在截断或提升,隐式类型转换在中间会就会产生临时变量 

所以,在上图当中可以当作 d 中的 5 拷贝给了临时变量 然后又给了int,临时变量具有常性,不可更改,即需要加上const使权限一样

所以取别名的原则是,对原引用变量,权限只能缩小(或不变),不能放大

int main()
{
   const int x = 20;//加上const,此时x是只读
   int& y = x;//int是可读可写,权限放大
   const int& y = x;//权限不变
   int c = 30;
   const int& d = c;//权限缩小,只读
   return 0;
}

后面学习类和对象后,我们传参如果写成普通引用,很多参数无法传递

void func(int& x)//void func(const int& x)
{}
int main()
{
   int a = 10;
   double d = 5.5;
   int f = d;
   const int& e = d;

   func(a);
   func(d);//此时往下权限放大都传不了,在上面func函数要使用const
   func(e);
   return 0;
}

四.引用的使用场景

1.先看一个传值、传引用的效率比较程序

#include 
struct A { int a[10000]; };
void TestFunc1(A aa) {}
void TestFunc2(A& aa) {}
void TestRefAndValue()
{
	A a;
	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc1(a);
	size_t end1 = clock();
	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc2(a);
	size_t end2 = clock();
	// 分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

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

运行结果:

TestFunc1(A)-time:7
TestFunc2(A&)-time:0

当参数或者返回值类型非常大时,用值作为参数或者返回值类型,效率是非常低下的

2.引用做输出型参数,减少拷贝,提高效率

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

2.引用做返回值(重点)

先看一下下面这个函数,根据我们所了解的C语言普通传值,函数运行完后会自动销毁栈帧

int count()
{
  int n = 0;
  n++;
  //...
  return 0;
}

所以我们后来学习了静态或全局变量,在前面加上static

int count()
{
  static int n = 0;
  n++;
  //...
  return 0;
}

但是注意:在项目当中,因为后面涉及到线程安全的问题,不太常用静态或全局变量

最后我们选择引用来做返回值

int& count()
{
   static int n = 0;
   n++;
   return n;//返回的是n的别名,不会再临时产生临时变量
}
int main()
{
	cout << count() << endl;
	cout << count() << endl;
	cout << count() << endl;	
}

做返回值和上面隐式类型转换一样,同样会产生临时变量,增加拷贝,使用引用做返回值就会减少拷贝,提高效率,这在后面STL学习当中会经常用到

五. 引用和指针的区别

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

从语法角度深入分析:

int main()
{
	int a = 10;
	//语法角度而言:ra是a的别名,没有额外开空间

	int& ra = a;
	ra = 20;
	//语法角度而言:pa存储a的地址,pa开了4/8字节空间
	int* pa = &a;
	*pa = 20;
	return 0;
}

从底层角度深入分析:

	int a = 10;
00007FF71CB01F7D  mov         dword ptr [a],0Ah  
	//语法角度而言:ra是a的别名,没有额外开空间

	int& ra = a;
00007FF71CB01F84  lea         rax,[a]  
00007FF71CB01F88  mov         qword ptr [ra],rax  
	ra = 20;
00007FF71CB01F8C  mov         rax,qword ptr [ra]  
00007FF71CB01F90  mov         dword ptr [rax],14h  
	//语法角度而言:pa存储a的地址,pa开了4/8字节空间
	int* pa = &a;
00007FF71CB01F96  lea         rax,[a]  
00007FF71CB01F9A  mov         qword ptr [pa],rax  
	*pa = 20;
00007FF71CB01F9E  mov         rax,qword ptr [pa]  
00007FF71CB01FA2  mov         dword ptr [rax],14h  

	return 0;
00007FF71CB01FA8  xor         eax,eax  

我们发现在底层实现上实际是有空间的,因为引用是按照指针方式来实现的,初学者可以先把引用理解为指针

希望这篇文章大家有所收获,我们下篇见

暑期编程PK赛 得CSDN机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1015930.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号