C++引用详解

文章目录

  • 1.变量的别名
  • 2.变量的多个别名
  • 3.引用存在的价值
  • 4.引用的大小
  • 5.从汇编角度看引用
  • 6.结构体的引用
  • 7.指针的引用
  • 8.常引用
    • 8.1 const引用可以指向临时数据
    • 8.2 const引用可以指向不同类型的数据
    • 8.3 const引用作为函数参数
  • 9.数组的引用

1.变量的别名

在 C 语言中,使用指针(Pointer)可以间接获取、修改某个变量的值。在 C++ 中,使用引用(Reference)可以起到跟指针类似的功能。

引用相当于是变量的别名,一般用 & 符号表示,基本数据类型、枚举、结构体、类、指针、数组等都可以有引用。

引用必须绑定到变量上去,不能绑定到常量上去。引用指向的类型要相同。

对引用做计算,就是对引用所指向的变量做计算。

在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”。

#include 
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 
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 
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 
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 
using namespace std;

struct Student {
	int age;
};

int main() {
	cout << sizeof(Student) << endl;
	// x64(64bit): 4
	// x86(32bit): 4

	return 0;
}

举例2:只含一个 int 指针的类对象的大小

#include 
using namespace std;

struct Student {
	int *age;
};

int main() {
	cout << sizeof(Student) << endl;
	// x64(64bit): 8
	// x86(32bit): 4

	return 0;
}

举例3:只含一个 int 引用的类对象的大小

#include 
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 
using namespace std;

int main() {
	
	int age = 10;

	// *p就是age的别名
	int *p = &age;
	*p = 30;

	return 0;
}

C++引用详解_第1张图片

举例2:查看 int 引用的汇编代码

#include 
using namespace std;

int main() {
	
	int age = 10;

	// ref就是age的别名
	int &ref = age;
	ref = 30;

	return 0;
}

C++引用详解_第2张图片

对比两图可以发现,ref 本质上就是指针,存储的就是 age 的地址值。

6.结构体的引用

#include 
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 
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 
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;
}

C++引用详解_第3张图片

举例2:指针的引用

#include 
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;
}

C++引用详解_第4张图片

8.常引用

引用可以被 const 修饰,这样就无法通过引用修改数据了,可以称为常引用。

const 必须写在 & 符号的左边,才能算是常引用。

#include 
using namespace std;

int main() {

	int height = 20;
	int age = 10;

	// p1不能修改指向,但是可以利用p1间接修改所指向的变量
	int* const p1 = &age;
	//p1 = &height; // 报错
	*p1 = 30;
	cout << age << endl; // 30

	// ref1不能修改指向,但是可以通过ref1间接修改所指向的变量
	int & const ref1 = age;
	ref1 = 40;
	cout << age << endl; // 40

	// p2可以修改指向,但是不可以利用p2间接修改所指向的变量
	int const* p2 = &age;
	p2 = &height;
	//*p2 = 30; // 报错

	// ref2不能修改指向,也不可以通过ref2间接修改所指向的变量
	int const &ref2 = age; // 常引用
	//ref2 = 40; // 报错

	return 0;
}

8.1 const引用可以指向临时数据

举例1:const引用指向常量

#include 
using namespace std;

int main() {

	const int &ref = 30;

	return 0;
}

举例2:const引用指向表达式

#include 
using namespace std;

int main() {

	int a = 1;
	int b = 2;
	const int &ref = a + b;

	return 0;
}

举例3:const引用指向函数返回值

#include 
using namespace std;

int func() {
	return 8;
}

int main() {

	const int &ref = func();

	return 0;
}

8.2 const引用可以指向不同类型的数据

#include 
using namespace std;

int main() {
	int age = 10;

	const double &ref = age;

	return 0;
}

当常引用指向了不同类型的数据时,会产生临时变量,即引用指向的并不是初始化时的那个变量。

举例1:查看常引用指向相同类型数据的汇编代码

#include 
using namespace std;

int main() {

	int age = 10;
	const int &ref = age;
	age = 30;

	cout << age << endl; // 30
	cout << ref << endl; // 30

	return 0;
}

C++引用详解_第5张图片

举例2:查看常引用指向不同类型数据的汇编代码

#include 
using namespace std;

int main() {

	int age = 10;
	const long &ref = age;
	age = 30;

	cout << age << endl; // 30
	cout << ref << endl; // 10

	return 0;
}

C++引用详解_第6张图片

8.3 const引用作为函数参数

const引用作为函数参数时,可以接受const和非const实参。

非const引用作为函数参数时,只能接受非const实参。

const引用跟非const引用可以构成函数重载。

const引用作为函数参数时的上述规则也适用于const指针。

#include 
using namespace std;

int sum(int &v1, int &v2) {
	cout << "sum(int &v1, int &v2)" << endl;
	return v1 + v2;
}

int sum(const int &v1, const int &v2) {
	cout << "sum(const int &v1, const int &v2)" << endl;
	return v1 + v2;
}

int main() {

	// 非const实参
	int a = 10;
	int b = 20;
	sum(a, b);
	
	// const实参
	const int c = 10;
	const int d = 20;
	sum(c, d);
	
	sum(10, 20);

	return 0;
}

输出结果如下:

C++引用详解_第7张图片

9.数组的引用

举例1:区分指针数组、数组指针、数组的引用

#include 
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 
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;
}

你可能感兴趣的