【C++】引用

1、基本概念

引用变量是C++新增的复合类型;
引用是已定义的变量的别名;
引用的主要用途是用作函数的形参和返回值;

2、语法

声明/创建引用的语法:数据类型 &引用名 = 原变量名。

3、注意事项

  • 引用的数据类型与原变量名的数据类型相同;
  • 引用名和变量名可以互换,它们的地址和值都完全相同;
  • 必须在声明引用的时候初始化,初始化后的地址和对应的原变量名不可改变;
  • C和C++用&符号来指示/取变量的地址,C++给&符号赋予了另一种含义。

例题

#include <iostream>
using namespace std;

int main()
{
	int a = 8;
	int& ra = a;
	cout << "a的值为:" << a <<"\t"<< "a的地址为:" << &a << endl;
	cout << "ra的值为:" << ra << "\t" << "ra的地址为:" << &ra << endl;
	int b = 5;
	ra = b;//等号运算符本质是赋值,相当于ra=5;
	cout << "b的值为:" << b << "\t" << "b的地址为:" << &b << endl;
	cout << "ra的值为:" << ra << "\t" << "ra的地址为:" << &ra << endl;

	return 0;
}

运行结果如下:

a的值为:8      a的地址为:000000F3D55AFC04
ra的值为:8     ra的地址为:000000F3D55AFC04
b的值为:5      b的地址为:000000F3D55AFC44
ra的值为:5     ra的地址为:000000F3D55AFC04

C:\code\day\x64\Debug\day.exe (进程 28012)已退出,代码为 0。
按任意键关闭此窗口. . .

4、引用的本质

引用是指针常量的伪装。
简单来说指针常量与变量的地址“连锁”,可以通过解引用改变地址的内容,但存储的地址没变。
具体内容可查看指针知识全解

5、引用用于函数的参数

1)概念

把函数的形参声明为引用,调用函数的时候,形参将成为实参的别名。
这种方法也叫做按引用传递或传引用。(传值、传地址、传引用只是说法不同,其实都是传值。)
引用的本质是指针,传递的是变量的地址,在函数中,修改形参会影响实参。

例题:

#include <iostream>
using namespace std;

void func1(int** p)//传地址,实参是指针p的地址,形参是二级指针
{
	*p = new int(8);//p是二级指针,存放指针的地址
	cout << "func1内存的地址为:" << *p << "\t" 
		<< "内存的值为:" << **p << endl;
}

void func2(int*& p)//传引用,实参是指针,形参是指针的别名
{
	p = new int(8);
	cout << "func2内存的地址为:" << p << "\t"
		<< "内存的值为:" << *p << endl;
}

int main()
{
	int* p = nullptr;//存放在子函数中动态分配的地址

	func1(&p);//传地址,实参填指针p的地址
	//func2(p);//传引用,实参填指针p

	cout << "main内存的地址为: " << p << "\t"
		<< "内存的值为:" << *p << endl;
	return 0;
}

运行结果如下(func1):

func1内存的地址为:0000011D3B66B5C0     内存的值为:8
main内存的地址为: 0000011D3B66B5C0     内存的值为:8

C:\code\day\x64\Debug\day.exe (进程 19196)已退出,代码为 0。
按任意键关闭此窗口. . .

运行结果如下(func2):

func2内存的地址为:00000267616E5160     内存的值为:8
main内存的地址为: 00000267616E5160     内存的值为:8

C:\code\day\x64\Debug\day.exe (进程 28960)已退出,代码为 0。
按任意键关闭此窗口. . .

2)优点

  • 传引用的代码更简洁;
  • 传引用不必使用二级指针;
  • 引用的属性和特别指出(看下一节内容)

3)引用的属性和特别指出(引用的参数和const)

如果引用的数据对象类型不匹配,当引用为const时,C++将创建临时变量,让引用指向临时变量。解释为:如果函数的实参不是左值或与const引用形参的类型不匹配,那么C++将创建正确类型的匿名变量,将实参的值传递给匿名变量,并让形参来引用该变量。

例如:

	const int& ra = 8;
	//第一行代码可分解为以下两行
	int a = 8;
	const int& ra = a;

左值:左值是可以被引用的数据对象,可以通过地址访问它们,例如:变量、数组元素、结构体成员、引用和解引用的指针。
非左值包括字面常量(用双引号包含的字符串除外)和包含多项的表达式。

何时创建临时变量

  • 引用是const;
  • 数据对象的类型是正确的,但不是左值;
  • 数据对象的类型不正确,但可以转换为正确的类型;

例:

#include <iostream>
using namespace std;

void funct(const int & ra,const string & str)
{
	//注意:不能修改ra和str的内容
	cout << "有请" << ra << "号" << str << endl;
}

int main()
{
	funct('X',"小马哥");//函数的实参'X'和"小马哥"均是非左值,可通过const来调整

	return 0;
}

运行结果如下:

有请88号小马哥

C:\code\day\x64\Debug\day.exe (进程 17756)已退出,代码为 0。
按任意键关闭此窗口. . .

将引用形参声明为const的理由有三个:

  • 使用const可以避免无意中修改数据的编程错误;
  • 使用const使函数能够处理const和非const实参,否则将只能接受非const实参;
  • 使用const,函数能正确生成并使用临时变量。

5、引用用于函数的参数

传统的函数返回机制与值传递类似。
函数的返回值被拷贝到一个临时位置(寄存器或栈),然后调用者程序再使用这个值。

double a = sqrt(36);

sqrt(36)的返回值6被拷贝到临时的位置,然后赋值给a。

例:

#include <iostream>
using namespace std;
//int a = 8;
int & funct()
{
	int a = 8;
	//static int a = 8;
	return a;
}

int main()
{
	int& b = funct();
	cout << "b = " << b << endl;

	return 0;
}

运行结果如下:

b = -858993460

C:\code\day\x64\Debug\day.exe (进程 21908)已退出,代码为 0。
按任意键关闭此窗口. . .

分析:程序正常退出,说明编译没有出错,但是结果好像不太对。函数funct的目的是想把变量a作为其他变量b的别名。变量b有幸成为了a的别名,但是不幸的是,调用完函数funct之后,变量a就不存在了,这是因为变量a是函数funct的局部变量,函数结束之后,a占用的内存就被释放了。
引用的本质是指针,变量a不存在了,引用b就成了野指针。

如果返回的是一个结构体,将会把整个结构拷贝到临时的位置。返回引用或指针则不会拷贝内存。

语法:返回值的数据类型 &函数名(形参列表)

注意

  • 如果返回局部变量的引用,其本质是野指针;
  • 可以返回函数的引用参数、类的成员、全局变量、静态变量;

以返回函数的引用参数举例:

#include <iostream>
using namespace std;

int & funct(int & ra)
{
	ra++;
	cout << "ra的地址为:" << &ra << "  ra的值为:" << ra << endl;
	return ra;
}

int main()
{
	int a = 8;
	cout << "a的地址为: " << &a << "  a的值为: " << a << endl;

	int& b = funct(a);
	cout << "b的地址为: " << &b << "  b的值为: " << b << endl;
	cout << "a的地址为: " << &a << "  a的值为: " << a << endl;

	return 0;
}

运行结果如下:

a的地址为: 000000C4623FF8A4  a的值为: 8
ra的地址为:000000C4623FF8A4  ra的值为:9
b的地址为: 000000C4623FF8A4  b的值为: 9
a的地址为: 000000C4623FF8A4  a的值为: 9

C:\code\day\x64\Debug\day.exe (进程 15312)已退出,代码为 0。
按任意键关闭此窗口. . .

分析:a、ra、b互为变量的别名,相同地址,相同地址的内容

  • 返回引用的函数名是被引用变量的别名,将const用于引用的返回类型。
#include <iostream>
using namespace std;

//const int & funct(int & ra) //引用值不能被修改,funct(a) = 20;会编译出错
int & funct(int & ra)
{
	ra++;
	cout << "ra的地址为:" << &ra << "  ra的值为:" << ra << endl;
	return ra;
}

int main()
{
	int a = 8;
	cout << "a的地址为: " << &a << "  a的值为: " << a << endl;

	int& b = funct(a); //函数加const,定义引用也需修改为const int& b = funct(a);
	cout << "b的地址为: " << &b << "  b的值为: " << b << endl;
	cout << "a的地址为: " << &a << "  a的值为: " << a << endl;
	//此时a的值为9
	funct(a) = 20;//先执行funct(a)的内容,之后再把20赋值给ra
	cout << "b的地址为: " << &b << "  b的值为: " << b << endl;
	cout << "a的地址为: " << &a << "  a的值为: " << a << endl;

	return 0;
}

运行结果如下:

a的地址为: 0000002BB66FF9A4  a的值为: 8
ra的地址为:0000002BB66FF9A4  ra的值为:9
b的地址为: 0000002BB66FF9A4  b的值为: 9
a的地址为: 0000002BB66FF9A4  a的值为: 9
ra的地址为:0000002BB66FF9A4  ra的值为:10
b的地址为: 0000002BB66FF9A4  b的值为: 20
a的地址为: 0000002BB66FF9A4  a的值为: 20

C:\code\day\x64\Debug\day.exe (进程 13416)已退出,代码为 0。
按任意键关闭此窗口. . .

分析:执行程序的先后顺序

6、各种形参的使用场景

1)不需要在函数中修该实参:

  • 如果实参很小,如内置数据类型或小型结构体,则按值传递;
  • 如果实参是数组,则使用const指针,因为这是唯一的选择(没有为数组建立引用的说法);
  • 如果实参是比较大的结构,则使用const指针或const引用;
  • 数据实参是类,则使用const引用,传递类的标准方式就是按引用传递;(类设计的语言常常要求使用引用)

2)需要在函数中修该实参:

  • 如果实参是内置数据类型,则使用指针。只要看到funct(&a)的调用,表示函数将修改变量a的值。
  • 如果实参是数组,则只能使用指针;
  • 如果实参是结构体,则使用指针或引用(推荐引用);
  • 如果实参是类,则使用引用。
  • 13
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值