C++:引用详解

本文详细介绍了C++中引用的相关知识,包括引用的基本概念、声明方法和注意事项,对比了函数的三种传参和返回值方式,阐述了常引用的特点,说明了引用作为返回值的规则,最后总结引用本质及目的,强调定义引用参数时加const保证数据安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++:引用详解

一、引用简说

1、引用:对地址(变量)取别名,对引用的操作与对原变量的直接操作效果完全一样。注意与指针的区别。

2、声明方法:类别标识符 &引用名 = 目标变量名

3、声明引用时,必须同时对其初始化。

4、引用声明一旦初始化,不能再将该引用名作为别的引用名的别名。

5、对引用名求地址,就是对目标变量求地址,即&r_var与&var相等。

6、不能建立数组的引用。

#include <iostream>
using namespace std;

int main()
{
    int var = 10;
    int tmp = 20;
    int *p_var = &var; // 在栈上开辟一块新的指针内存,用于存放变量var的地址。
    // int &r_var; // 错误声明,未初始化。
    // 定义引用r_var,它是变量var的引用,即别名。未开辟新内存,r_var直接指向var的地址。
    int &r_var = var; 
    // 赋值操作,将tmp的值拷贝一份到r_var(var)所在内存,此时var与r_var的值都为20.
    r_var = tmp;
    return 0;
}

二、函数三种传参方式和三种返回值方式

1、值传递:形参是对实参的一份临时拷贝,形参内存中存放的是实参的值,执行交换时,交换的是形参,因此不会交换实参。

#include <iostream>
using namespace std;

void swap(int var01, int var02)
{
	int tmp = var01;
	var01 = var02;
	var02 = tmp;
}

2、址传递:形参是对实参的一份临时拷贝,形参内存中存放的是实参的地址,执行交换操作时,交换的是实参(解引用),因此实现了实参交换。

void swap(int *p_var01, int *p_var02)
{
	int tmp = *p_var01;
	*p_var01 = *p_var02;
	*p_var02 = tmp;
}

3、引用传递:形参执行的是引用操作,形参r_var01与实参指向同一块内存,形参r_var02与实参指向同一块内存,执行交换操作时,交换的是实参。

void swap(int &r_var01, int &r_var02)
{
	int tmp = r_var01;
	r_var01 = r_var02;
	r_var02 = tmp;
}

4、函数返回变量值:与函数传参值传递原理相同。在函数结束返回时,将局部变量值拷贝一份给临时变量,然后将该临时变量返回给调用函数。

#include <iostream>
using namespace std;

int test(int var)
{
	int a = var * var;
	return a;
}

int main()
{
    int a = 10;
    int s = test(10);
    cout<<"s = "<<s<<endl; // 打印100.
    return 0;
}

5、函数返回变量地址:与函数传参址传递原理相同。调用函数结束时,开辟一块int*型临时内存空间(匿名)用于存放局部变量a的地址,释放局部变量a的内存,再将匿名临时内存空间中的数据(局部变量a的地址)拷贝一份到指针变量s的内存地址中。由于函数结束时,函数内的所有局部变量内存都已被回收,因此再利用指针s去读取局部变量a中的数据自然不成功。

#include <iostream>
using namespace std;

int* test(int var)
{
	int a = var * var;
	return &a;
}

int main()
{
    // [Warning] address of local variable 'a' returned [-Wreturn-local-addr]
    int* s = test(10);
    return 0;
}
	

6、函数返回变量的引用:调用函数结束时,释放局部变量a的内存,通过引用对局部变量a取别名为s。由于局部变量a的内存已经释放,因此再利用别名去读取已被释放内存空间中的数据自然不能成功。

#include <iostream>
using namespace std;

int& test(int var)
{
	int a = var * var;
	return a;
}

int main()
{
    // [Warning] address of local variable 'a' returned [-Wreturn-local-addr]
    int& s = test(10);
    return 0;
}

三、常引用

1、声明方法:const 类别标识符 &引用名 = 目标变量名

2、const声明的引用,不能通过引用对目标变量的值进行修改。

int main()
{
    int var = 10;
	const int &p_var = var;
	// p_var = 20; // 错误,p_var 为常引用变量,不能对目标变量进行修改。
	var = 20; // 正确。
    return 0;
}

3、通过常引用声明的函数形参,可直接调用常量。引用型参数应在能被定义为const的情况下,尽可能定义为const。

#include <iostream>
using namespace std;

void test01(int &var)
{
	cout<<var<<endl;
}
	
void test02(const int &var)
{
	cout<<var<<endl;
}

int main()
{
    int a = 10;
    test01(a); // 正确,打印10.
    // [Error] invalid initialization of non-const reference of type 'int&' from an 
    
    // rvalue of type 'int'
    test01(10); 
    test02(a); // 正确,打印10.
    test02(10); // 正确,打印10.
    return 0;
}

四、引用作为返回值

1、引用函数作为返回值,须在函数声明及函数定义返回类型标识符后函数名前加上&。

函数声明:类型标识符 &函数名(形参列表及类型说明);

函数定义:类型标识符 &函数名(形参列表及类型说明){函数体}

2、不能返回临时变量的引用。具体解释参考上面函数返回变量的引用部分。

3、若函数的返回值是引用,则该函数的调用可以作为赋值表达式的左值。

#include <iostream>
using namespace std;

int tmp; // tmp为全局变量。
	
int test01(int var)
{
	tmp = var * var;
	return tmp;
}
	
int &test02(int var)
{
	tmp = var * var;
	return tmp;
}

int main()
{
	int a = 10;
	// 系统生成返回值tmp的一份副本(匿名),将匿名副本中的数据拷贝到s内存中。
	// s和tmp各有的内存。
	int s = test01(a);
	// 系统生成返回值tmp的一份临时副本(匿名),通过引用对匿名副本内存空间取别名s。
	// 不能对匿名临时副本空间取别名。该行代码非法。
	// int &s = test01(a);
	// 系统不生成返回值tmp的副本,将全局变量tmp中的数据拷贝到s内存中。s和tmp各有
	// 各的内存。
	int s = test02(a);
	// 系统不生成返回值tmp的副本,通过引用对全局变量tmp取别名s。s和tmp为同一块内
	// 存区。
	int &s = test02(a);
	cout<<s<<endl;
    return 0;
}

五、总结

1、引用的本质是一个指针常量(int* const p),因此声明引用时必须初始化,且之后不可修改其指向;

2、引用的目的主要用于在函数参数传递中,解决大块数据或对象传递效率和空间不如意的问题,同时可以避免利用指针实现该功能的繁琐。在定义引用参数时,尽可能加上关键字const以保证传入数据的安全性(保证传入数据不在函数内部被修改)。

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值