1.变量的别名
在 C 语言中,使用指针(Pointer)可以间接获取、修改某个变量的值。在 C++ 中,使用引用(Reference)可以起到跟指针类似的功能。
引用相当于是变量的别名,一般用 &
符号表示,基本数据类型、枚举、结构体、类、指针、数组等都可以有引用。
引用必须绑定到变量上去,不能绑定到常量上去。引用指向的类型要相同。
对引用做计算,就是对引用所指向的变量做计算。
在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”。
#include <iostream>
using namespace std;
int main() {
int age = 10;
int height = 20;
// 定义了一个age的引用,ref相当于是age的别名
int &ref = age;
ref = 20;
ref += 30;
cout << age << endl; // 50
ref = height;
ref = 11;
cout << age << endl; // 11
cout << height << endl; // 20
return 0;
}
2.变量的多个别名
可以利用引用初始化另一个引用,相当于某个变量的多个别名。
#include <iostream>
using namespace std;
int main() {
int age = 10;
// 定义了一个age的引用,ref相当于是age的别名
int& ref = age;
int& ref1 = ref;
int& ref2 = ref1;
ref += 10;
ref1 += 10;
ref2 += 10;
cout << age << endl; // 40
return 0;
}
3.引用存在的价值
不存在 “引用的引用”、“指向引用的指针”、“引用数组”。
引用存在的价值之一:比指针更安全、函数返回值可以被赋值。
举例1:通过指针传参交换两个数
#include <iostream>
using namespace std;
void swap(int *v1, int *v2) {
int tmp = *v1;
*v1 = *v2;
*v2 = tmp;
}
int main() {
int a = 10;
int b = 20;
swap(&a, &b);
cout << "a = " << a << ", b = " << b << endl; // a = 20, b = 10
return 0;
}
举例2:通过引用传参交换两个数
#include <iostream>
using namespace std;
void swap(int &v1, int &v2) {
int tmp = v1;
v1 = v2;
v2 = tmp;
}
int main() {
int a = 10;
int b = 20;
swap(a, b);
cout << "a = " << a << ", b = " << b << endl; // a = 20, b = 10
int c = 2;
int d = 3;
swap(c, d);
cout << "c = " << c << ", d = " << d << endl; // c = 3, d = 2
return 0;
}
4.引用的大小
一个引用占用一个指针的大小。
举例1:只含一个 int 整数的类对象的大小
#include <iostream>
using namespace std;
struct Student {
int age;
};
int main() {
cout << sizeof(Student) << endl;
// x64(64bit): 4
// x86(32bit): 4
return 0;
}
举例2:只含一个 int 指针的类对象的大小
#include <iostream>
using namespace std;
struct Student {
int *age;
};
int main() {
cout << sizeof(Student) << endl;
// x64(64bit): 8
// x86(32bit): 4
return 0;
}
举例3:只含一个 int 引用的类对象的大小
#include <iostream>
using namespace std;
struct Student {
int &age;
};
int main() {
cout << sizeof(Student) << endl;
// x64(64bit): 8
// x86(32bit): 4
return 0;
}
5.从汇编角度看引用
引用的本质就是指针,只是编译器削弱了它的功能,所以引用就是弱化了的指针。
加断点,按 F5 进入调试,转到反汇编,不勾选“显示符号名”。
举例1:查看 int 指针的汇编代码
#include <iostream>
using namespace std;
int main() {
int age = 10;
// *p就是age的别名
int *p = &age;
*p = 30;
return 0;
}
举例2:查看 int 引用的汇编代码
#include <iostream>
using namespace std;
int main() {
int age = 10;
// ref就是age的别名
int &ref = age;
ref = 30;
return 0;
}
对比两图可以发现,ref
本质上就是指针,存储的就是 age
的地址值。
6.结构体的引用
#include <iostream>
using namespace std;
struct Date {
int year;
int month;
int day;
};
int main() {
Date d = { 2022, 1, 25 };
Date& ref = d;
ref.year = 2023;
cout << d.year << endl; // 2023
return 0;
}
7.指针的引用
指针是一个存放地址的变量,而指针引用指的是这个指针变量的引用。
#include <iostream>
using namespace std;
int main() {
int age = 10;
int *p = &age;
int *&ref = p;
*ref = 30;
cout << age << endl; // 30
int height = 40;
ref = &height;
cout << *ref << endl; // 40
return 0;
}
在 C++ 中,如果参数不是引用的话,会调用参数对象的拷贝构造函数,所以如果想改变指针所指的对象(即想要改变指针里面存的地址),就要使用指针引用。
举例1:指针
#include <iostream>
using namespace std;
struct Point
{
int x;
int y;
};
void change1(Point* pp)
{
pp = new Point;
pp->x = 4;
}
int main()
{
Point* p = new Point;
p->x = 10;
cout << "指针前:" << p->x << endl;
change1(p);
cout << "指针后:" << p->x << endl;
return 0;
}
举例2:指针的引用
#include <iostream>
using namespace std;
struct Point
{
int x;
int y;
};
void change2(Point* &pp)
{
pp = new Point;
pp->x = 4;
}
int main()
{
Point* p = new Point;
p->x = 10;
cout << "指针引用前:" << p->x << endl;
change2(p);
cout << "指针引用后:" << p->x << endl;
return 0;
}
8.数组的引用
举例1:区分指针数组、数组指针、数组的引用
#include <iostream>
using namespace std;
int main() {
int array[3] = {1, 2, 3};
// 指针数组,数组里面可以存放3个int*
int *arr1[3] = {array, array, array};
// 数组指针,用于指向数组的指针
int (*arr2)[3] = &array;
// 数组的引用
int (&ref)[3] = array;
ref[0] = 10;
cout << array[0] << endl; // 10
return 0;
}
举例2:引用数组的两种格式
#include <iostream>
using namespace std;
int main() {
// 数组名arr其实是数组的地址,也是数组首元素的地址
// 数组名arr可以看做是指向数组首元素的指针(int *)
int arr[] = {1, 2, 3};
cout << *(arr + 2) << endl; // 3
// 等价于arr[2]
// 数组的引用格式1
int (&ref)[3] = arr;
cout << ref[2] << endl; // 3
// 数组的引用格式2
int * const &refArr = arr;
cout << refArr[2] << endl; // 3
return 0;
}