C++中引用的本质到底是什么?

C++的引用到底是什么?用了这么久,还不知道它居然也是个指针…

相关文章
C++编程之命名空间、const常量的总结
C++编程之运算符重载

前段时间写过一篇《C++编程之引用的详细总结》 ,看过就知道,哦,原来引用是对象/变量的一个别名,在使用时,是直接操作对象本体,因此通过引用传参,不需要拷贝内存,效率很高。但是最近有人私下问我:“你写的倒是挺全面的,但是引用的本质到底是什么?”

因此,今天决定再深入解释一下引用。

其实 引用的本质在C++内部实现是一个指针常量。C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小和指针相同,这个过程是编译器内部实现,用户不可见。

#include<iostream>
using namespace std;

// 编译器判断是引用,会将入参自动转换成int* const ref = &a;
void Test(int &ref)
{
	ref = 10;    // ref是引用,此处会转换为 *ref = 10;
}

int main()
{
	int a = 100;
	int & b = a; //自动转换为int* const b = &a;这就说明为什么引用必须要初始化。
	b = 20; //编译器判断b是引用,自动转换为*b = 20;
	Test(a);
	return 0 ;
}

这就是引用为什么必须要初始化,因为内部转换为常指针时,需要拿到它的地址,所以必须要先初始化。
接着通过反汇编,验证两种情况是等价的。

使用引用传参

#include<iostream>

using namespace std;


void Test(int& a)
{
	a = 100;
}

int main()
{
	int b = 10;
	Test(b);
	cout << "b = " << b << endl;
	return 0;
}

运行结果
在这里插入图片描述
断点调试,进入汇编

00F52AF8 mov eax,dword ptr [a]   //dword表示的是双字,四字节。a中保存的是内存中的地址。将该地址处的4字节数据传送到eax中。
00F52AFB mov dword ptr [eax],64h //将64h的值传递给 [eax] 所指示的内存单元,也就是a的本体

在这里插入图片描述
如图显示断点到 a = 100 之前,a的值是10.,执行之后,如下图所示。
在这里插入图片描述
这样就通过操作引用,实际修改了本体的值。

使用常指针传参

#include<iostream>
using namespace std;

void Test1(int* const a)
{
	*a = 1000;
}

int main()
{
	int b = 10;
	Test1(&b);
	cout << "b = " << b << endl;
	system("pause");
	return 0;
}

运行结果
在这里插入图片描述
同样,断点调试到汇编中看看结果

001D2A98 mov eax,dword ptr [a]       //a中保存的是内存中的地址。将该地址处的4字节数据传送到eax中。同样是双字节dwod,前面引用也是双字节,说明引用内部就是常指针。
001D2A9B mov dword ptr [eax],3E8h    // 将3E8h的值传递给 [eax] 所指示的内存单元,也就是a的本体

如下图所示
在这里插入图片描述
当执行 a=1000 后,a的值也变为1000,如下图汇编代码:
在这里插入图片描述
因此,从汇编层看,引用在内部的确被转换为常指针。这就解释了引用必须要初始化的原因,初始化后,不能再修改指向。因此可以通过引用, 间接代替指针 ,比如一级指针可以直接用引用代替,二级指针可以用一级指针的引用代替,三级指针可以用二级指针的引用代替等。

总结完引用的本质,接着补充几个实际的例子。

数组的引用

void Test()
{
	int arr[10];
	int (&pArr)[10] = arr;
	for(int i = 0; i< 10;i++)
	{
		arr[i]=i;
	}
	for(int i =0; i<10;i++)
	{
		cout<<pArr[i]<<endl;
	}
}

输出结果:
0
1
2
3
...
9

指针的引用

#include<iostream>
using namespace std;

class Person
{
public:
	int age;
}

// 使用二级指针给一级指针分配内存。可以通过指针的引用简化为一级指针,如下AllocSpace2
void AllocSpace1(Person **p)
{
	*p = (Person*)malloc(sizeof(Person));
	return;
}

// 使用指针的引用传参
void AllocSpace2(Person* &p)
{
	p = (Person*)malloc(sizeof(Person));
	return;
}

int main()
{
	Person* p = NULL;
	AllocSpace1(&p);  // 取地址,传入二级指针
	AllocSpace2(p);   // 引用,直接传入一级指针本体。
	return 0;
}

// 因此,可以使用引用简化指针,即二级指针可以用一级指针的引用代替,一级指针直接用引用代替,减少指针操作。
发布了16 篇原创文章 · 获赞 247 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Marble_ccp/article/details/105033373