C++中的引用【应用场景以及常引用的注意点】

什么是引用

类型& 引用变量名(对象名) = 引用实体;

#include <iostream>

using namespace srd;
int main()
{
	int x = 10;
	int& x1 = x;
	cout << "x = " << x << endl;
	cout << "int& x1 = x: " << x1 << endl;
	cout << "&x " << &x << endl;
	cout << "&x1 " << &x1 << endl << endl;
	return 0;
}

在这里插入图片描述
引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。


创建引用

类型& 引用变量名(对象名) = 引用实体;

#include <iostream>
 
using namespace std;
 
int main ()
{
   // 声明简单的变量
   int i;
   double d;
 
   // 声明引用变量
   int& r = i;
   double& s = d;
   
   i = 5;
   cout << "Value of i : " << i << endl;
   cout << "Value of i reference : " << r  << endl;
 
   d = 11.7;
   cout << "Value of d : " << d << endl;
   cout << "Value of d reference : " << s  << endl;
   
   return 0;
}

上面代码产生的结果:

Value of i : 5
Value of i reference : 5
Value of d : 11.7
Value of d reference : 11.7

引用类型

基本引用类型
#include <iostream>
using namespace std;
int main()
{
    int a = 3;
    int &b = a;  //b就是a的引用,即b是a的一个别名。
                 //引用必须初始化,否则编译会报错
   
    b = 10;  //修改b就是修改a,因为b是a的别名
    cout<< a << endl; //此时a 的值,已由原来的3变成了10.因为我们无论对别名做什么操作,其实都是对变量的本身做操作。
    return 0;
}
结构体类型的引用
#include <iostream>
using namespace std;
//定义一个名叫Coor的结构体,这是一个坐标。
typedef struct
{
    int x;
    int y;
}Coor;
 
int main()
{
    Coor c1;    //定义一个结构体变量,叫c1.
    Coor& c = c1;    //给c1起了一个别名叫c.
    c.x = 10;    //通过引用给结构体变量的数据成员赋值
    c.y = 20;
    cout<< c1.x << c2.y;    //输出的结果:10 20
    return 0;
}
指针类型的引用
#include <iostream>
using namespace std;
 
int main()
{
    int a = 10;    //定义一个整型的a变量,a的值为10。
    int *p = &a; //定义一个指向a变量的指针
    int*& q = p;    //定义一个指针的引用,即q为p的别名。 定义方法:类型 *&指针引用名 = 指针;
    *q = 20;    //把20赋给*q,相当于是把20赋值给*p,也就相当于把20赋值给a.
    cout<< a << endl;    //输出a 的值为20.
    return 0;
}

引用特性

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

在这里插入图片描述
在这里插入图片描述

//正确定义引用
int temp = 10;
int& temp1 = temp;
  1. 一个变量可以有多个引用

在这里插入图片描述
同样,这些引用都是temp的别名,修改temp或者修改这些引用就可以修改temp本身

  1. 引用一旦引用一个实体,再不能引用其他实体
#include <iostream>
using namespace std;
int main()
{
	int temp = 10;
	int& temp1 = temp;
	cout << "temp1 " << temp1 << endl;
	int temp2 = 11;
	temp1 = temp2;
	cout << "temp1 = temp2之后的结果" << temp1 << endl;
	return 0;
}

在这里插入图片描述
说明上面的temp1 = temp2仅仅是拷贝的意思,将temp2的值拷贝到引用里面,也就是相当于改变了temp的值,并不是将引用实体进行更换

引用的作用

作为函数的参数
#include <iostream>
using namespace std;
 
void swap(int& x, int& y)
{
   int temp = x; 
   x = y;    
   y = temp;
}
 
int main ()
{
   // 局部变量声明
   int a = 100;
   int b = 200;
 
   cout << "交换前,a 的值:" << a << endl;
   cout << "交换前,b 的值:" << b << endl;
 
   /* 调用函数来交换值 */
   swap(a, b);
 
   cout << "交换后,a 的值:" << a << endl;
   cout << "交换后,b 的值:" << b << endl;
 
   return 0;
}

上述代码执行结果

交换前,a 的值: 100
交换前,b 的值: 200
交换后,a 的值: 200
交换后,b 的值: 100

作为函数的参数(示例二)

单链表进行尾插

	typedef struct ListNode
	{
		int val;
		struct ListNode* next;
	}ListNode;

	void PushBack(struct LinkNode** head)
	{
		if (*head == NULL)
		{
			//malloc创建结点
		}
		else
		{
			//链接结点
		}
	}

这个时候必须使用二级指针,因为如果主函数里面的链表指针是NULL,那我们就要malloc头结点,使用引用的代码如下

	typedef struct ListNode
	{
		int val;
		struct ListNode* next;
	}ListNode;

	void PushBack(struct LinkNode*& head)
	{
		if (head == NULL)
		{
			//malloc创建结点
		}
		else
		{
			//链接结点
		}
	}

这里head指针直接就是函数实参的别名,直接就可以在函数调用里面修改指针


引用作函数的返回值(修改返回对象)
#include <iostream>
 
using namespace std;
 //全局变量:数组
double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0};
 
double& setValues(int i) {  
   double& ref = vals[i];    
   return ref;   // 返回第 i 个元素的引用,ref 是一个引用变量,ref 引用 vals[i]
 
 
}
 
// 要调用上面定义函数的主函数
int main ()
{
 
   cout << "改变前的值" << endl;
   for ( int i = 0; i < 5; i++ )
   {
       cout << "vals[" << i << "] = ";
       cout << vals[i] << endl;
   }
 
 //引用返回的是vals[1]和vals[3],通过 别名 直接可以修改数组元素
   setValues(1) = 20.23; // 改变第 2 个元素
   setValues(3) = 70.8;  // 改变第 4 个元素
 
   cout << "改变后的值" << endl;
   for ( int i = 0; i < 5; i++ )
   {
       cout << "vals[" << i << "] = ";
       cout << vals[i] << endl;
   }
   return 0;
}

上述代码的执行结果

改变前的值
vals[0] = 10.1
vals[1] = 12.6
vals[2] = 33.1
vals[3] = 24.1
vals[4] = 50
改变后的值
vals[0] = 10.1
vals[1] = 20.23
vals[2] = 33.1
vals[3] = 70.8
vals[4] = 50
注意点(反例)
#inlcude <iostream>

using namespace std;
int count()
{
	int n = 0;
	n++;
	return n;
}
int main()
{
	//正常打印 1
	int ret = count(); 
	//报错:error C2440: “初始化”: 无法从“int”转换为“int &”
	int& ret = count();
}

调整上述代码(仍然错误)

#inlcude <iostream>

using namespace std;
int& count()
{
	int n = 0;
	n++;
	return n;
}
int main()
{
	//将n的引用返回给ret
	int& ret = count(); 
	cout << ret << endl;
}

但是这里打印出来却是随机值:因为cout是函数,我们先从栈帧里面拿到ret的值,调用函数的时候先进行传参,然后建立cout栈帧,这里可能会覆盖之前n的内存地址,虽然count函数执行完栈帧就销毁了,里面的局部变量也就销毁了,但是局部的n的地址仍旧在,所以count函数返回的引用仍然是n那块的地址,里面的值我们不得而知

所以通过上面两个“引用作为返回值的”的例子我们可以知道:引用作为返回值得目的是想要修改返回对象。引用作为返回值的要求:出了函数作用域之后对象还在,还可以正常使用。就像上面count函数一样,它返回的是一个局部变量的引用,但是局部变量出了函数的作用域就会消失,这个就像是“野引用”一样,引用的变量是被回收的。一般全局变量、静态变量、或者malloc创建的节点空间使用引用作为返回值


常引用

#include <iostream>

using namespace std;
int main()
{
	//字面值是常量
	const int& p1 = 3.114;
	//错误  常量的引用必须是常量
	int& p2 = 3.114;
	
	//常量的引用必须是const
	const int p3 = 66;
	const int& p4 = p3;
	
	//错误  发生整形提升、截断、类型转换的时候编译器会产生一个临时对象,这个临时对象是const类型的
	int p5 = 13;
	double& p6 = p5; 

	//上述代码也就是:
	const double& temp =(double) 13;
	const double& p6 = temp;
	
	//所以修改后正确的代码:
	 int p5 = 13;
	const double& p6 = p5; 

	//注意上面的p6的内容是不会根据p5的改变而改变,因为p6是编译器自己创建的临时变量的引用,我们不知道在哪里、叫什么名字!
}

以C++的语义来说,如果一个程序员只想传递参数给函数,而不希望函数修改传入的参数时,那么,或者使用值传递,或者采用常量型引用(const &)。考虑到大对象复制时产生的开销,一般使用常量型引用const &。如果函数的参数是某个类型的一个非常量的引用,那就相当于告诉编译器,程序员希望得到函数对参数的修改结果。

临时变量是由编译器生成的,C++语言规范没规定编译器生成临时变量的规则,程序员无法得知由编译器生成的临时变量的名字,程序员无法访问那个临时变量。这意味着,以引用的方式传递一个临时变量做为函数参数,如果函数内部对此临时变量做了修改,那么函数返回后,程序员无法获得函数对临时变量的修改。函数对临时变量所做出的所有更改,都将丢失


在《C++函数的返回值》“当函数的返回值是非引用变量时,会用一个临时变量来保存该返回值;

当函数的返回值是引用变量时,不使用临时变量,直接返回该引用”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值