目录
一、引用的概念
引用是C++对C语言的重要扩展。
它的作用是为变量创建一个别名,通过这个别名我们可以直接操作原变量。比如你有一个本名(变量),同时有一个小名或者别称(引用),其他人通过本名或者小名都可以找到你。任何对引用的操作,实际上都是对原变量的操作。
示例:
int val= 1021;
int &p = val; //p是val的引用
p=415; //修改p的值会影响val
cout << "val=" << val<< endl; //打印出来的结果是 val=415
二、引用的基本语法
引用使用&作为标识符
基本格式:
数据类型 &引用名 = 引用的目标;
int a = 10;
int &r = a; // 定义一个 int 类型的引用 r,引用变量 a
三、引用的本质
引用的本质是 C++ 内部实现的 指针常量。因此,引用与指针有着相似之处,但它是更高层次的抽象,提供了更方便的操作方式。
定义引用时需要注意以下几点:
引用必须初始化:定义引用时,必须为其指定一个初始化目标。
int a = 10;
int &r = a; // r 必须在定义时初始化
引用的类型与目标类型必须一致:引用的类型必须与它所引用的变量类型保持一致。(继承和多态除外)
int a = 10;
int &r = a; // 类型一致
引用不可更改目标:一旦引用绑定到一个变量上,不能再更改其引用的目标。
下列程序是正确的,但是并不能说明:引用能重新赋值.很明显,引用是不能重新赋值的,只是理解上错了!
引用的赋值:是指引用初始化时,它的引用对象只能是变量,并且,一旦它指定为某一个对象的引用后,就不能更改了。
但是,可以用这个引用来改变它的对象的值,从而达到引用的目的——作为变量对象的别名。
如下例,引用p初始化为a,即p从此以后一直是a的引用,若想让p不再是a的引用而成为别的变量的引用那是不可能的。
所以,接下来的一句"p=b;"就不能理解成:取消p是a的引用而将p作为b的引用。
正确的理解应该是:利用引用p来改变它所指对象a的值,即相当于语句"p= 2048;"。
若在下示例语句"p = b;"后加上一句"b= 10",
结果将是:"a = 2048; b = 10; p = 2048",从这个结果就能很好理解了。
int a = 1021;
int b = 2048;
int &p = a;
p = b;
cout << "a=" << a << endl;
四、引用作为函数参数
作用:函数传参时,可以利用引用的技术让形参修饰实参
*优点:可以简化指针修改实参
示例
#include <iostream>
#include <string>
using namespace std;
void fun_swap_1(int val_1, int val_2)
{
int temp = val_1;
val_1 = val_2;
val_2 = temp;
}
void fun_swap_2(int *val_1, int *val_2)
{
int temp = *val_1;
*val_1 = *val_2;
*val_2 = temp;
}
void fun_swap_3(int val_1, int val_2)
{
int temp = val_1;
val_1 = val_2;
val_2 = temp;
}
int main(int argc, char const *argv[])
{
int val_1 = 415;
int val_2 = 526;
// 原始值
printf("原始值为:val_1=%d,val_2=%d\n", val_1, val_2);
// 值传递
fun_swap_1(val_1, val_2);
printf("值传递: val_1=%d,val_2=%d\n", val_1, val_2);//值传递不能交换两个变量的值
// 指针传
fun_swap_2(&val_1, &val_2);
printf("指针传递val_1=%d,val_2=%d\n", val_1, val_2);//指针传递能交换两个变量的值
// 引用传递
fun_swap_3(val_1, val_2);
printf("引用传递val_1=%d,val_2=%d\n", val_1, val_2);//引用传递能交换两个变量的值
return 0;
}
结果
原始值为:val_1=415,val_2=526
值传递: val_1=415,val_2=526
指针传递:val_1=526,val_2=415
引用传递:val_1=526,val_2=415
五、引用与返回值
我们平时使用函数时,函数的返回值都是一个右值。
但是引用作为函数的返回值时,返回值是一个左值。
- 左值:既可以放在等号左边,也可以放在等号右边的值。
- 右值:只能放在等号右边的值。
引用作为函数的返回值,不能返回局部变量的引用,因为局部变量在函数调用结束时就被回收了。
可以返回全局变量的引用或者 static
修饰的局部变量的引用。
示例1
#include <iostream>
using namespace std;
// 返回静态局部变量的引用
int &my_add(const int &x, const int &y) {
static int temp = x + y; // 静态局部变量
return temp; // 返回 temp 的引用
}
// 返回全局变量 value 的引用
int value = 100;
int &func() {
return value; // 返回 value 的引用
}
int main() {
// 返回 temp 的引用
int &r1 = my_add(10, 20); // r1 绑定到 temp
cout << r1 << endl; // 输出 30
// 返回全局变量 value 的引用
int &r2 = func(); // r2 绑定到 value
cout << r2 << endl; // 输出 100
// 修改 value 的值
r2 = 200;
cout << value << endl; // 输出 200
// 通过 func() 修改 value
func() = 300;
cout << value << endl; // 输出 300
return 0;
}
六、引用与结构体
当结构体中包含引用的成员时,定义结构体的对象时,必须对引用进行初始化。由于引用必须在创建时绑定到某个变量,因此不能延迟初始化。
1、引用结构:
2、结构体内部有引用:
初始化方式
MyStruct T1 = { .valur = 20 , .ref = val }; // 错误的写法,不能使用初始化列表的指定成员
MyStruct T1 = { 20 , val }; // 正确的写法,必须按照结构体成员顺序进行初始化
代码示例
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
struct MyStruct {
int valur; // 普通成员
int &ref; // 引用成员,必须在初始化时绑定
};
int main() {
int val = 10; // 用于初始化引用的变量
MyStruct T1 = { 20, val }; // 初始化结构体,包括引用成员
val = 20; // 修改引用绑定的变量的值
cout << T1.ref << endl; // 输出 20,因为引用指向 val
}
七、 引用与常量
当 const
修饰引用时,它被称为常引用。常引用的作用是禁止通过引用来修改其引用的目标变量。
作用:防止对引用目标的错误修改,保护数据的完整性。
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
int func_Add(const int &val_1, const int &val_2) {
return val_1 + val_2;
}
int main() {
// 变量
int val = 10;
// 引用 常量引用
cosnt int &ref = val;
// 常量引用 引用变量
cout << "ref = " << ref << endl;
// 常量引用 作为参数
int val_1 = 80;
int val_2 = 80;
cout << "val_1 + val_2 = " << func_Add(val_1, val_2) << endl;
// 常用引用 引用常量
// int & ref_1 = 10; // 不使用常量引用是不能引用常量的
const int & ref_1 = 10;
const int val_3 = 50;
// int & ref_2 = val_3; // 不使用常量引用是不能引用const 所修饰的常量
const int & ref_2 = val_3;
// 常量引用 引用临时值
// int & ref_3 = val_1 + val_2; // 不使用常量引用是不能引用临时值的
const int & ref_3 = val_1 + val_2;
}