引用进阶
引用的数据类型不同时的特殊情况
看下面代码,观察情况想一想?
#include <iostream>
using namespace std;
int main()
{
double a = 3.1415926;
const int& ra = a;
system("pause");
return 0;
}
- 我们常说:一个变量的引用就相当于给这个变量取别名,这个引用变量和变量表示同一块内存空间,其地址相同,内容相同,操作引用就相当于操作原变量。
但是从上述代码及现象中可以看出:引用和变量都有各自的空间,这是为什么?
-
根据先前对于引用的认知:ra和a的地址不一样,说明ra并不是a的别名,即ra不是a的引用
-
原因:其实是因为当ra去引用a时,编译器发现,ra和a的数据类型不一致,于是编译器就创建了一块临时空间给ra引用,这块临时空间中的数据无法修改,具有常量特性,这也就是为什么ra需要加上const修饰,如果没有const限定就无法通过编译。
启示:引用变量数据类型和被引用变量数据类型必须相同哦!
巧用引用使代码简化
通过对比两句代码观察引用的好处
#include <iostream>
using namespace std;
struct A
{
int a;
int b;
struct B
{
int c;
int d;
struct C{
int e;
int f;
};
C strC;
};
B strB;
};
int main()
{
A a;
a.strB.strC.e = 10;
int& re = a.strB.strC.e;
re = 20;
system("pause");
return 0;
}
这里将a.strB.strC.e被一个引用变量re代替,使得代码简化
巧用引用代替指针
实现两个数的交换
void swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
这里引用的作用和一级指针作用有异曲同工之妙
引用作为返回值出现的奇怪现象
#include <iostream>
using namespace std;
int& add(int a, int b)
{
int sum = a + b;
return sum;
}
int main()
{
int& sum = add(1, 2);
cout << "sum = " << sum << endl;
add(2, 3);
cout << "sum = " << sum << endl;
add(3, 4);
cout << "sum = " << sum << endl;
system("pause");
return 0;
}
输出结果:
这是为啥呢??????咋还会变呢???????
我们把代码变一变,看看这俩sum会不会有什么关系呢?
#include <iostream>
using namespace std;
int& add(int a, int b)
{
int sum = a + b;
cout << "<add> &sum = " << &sum << endl;
return sum;
}
int main()
{
int& sum = add(1, 2);
cout << "<main> &sum = " << &sum << endl;
system("pause");
return 0;
}
输出结果:
嗷嗷嗷!!!!!原来他们是同一块内存呀!!!!(吓到模糊)
正常来说:一个函数中的局部变量就是在函数执行结束时其生命结束,变量所在的那块地址上面的数据已经是无效数据,那块地址已经是非法空间了
虽说main函数中的引用变量指向的是add函数中局部变量sum开辟的空间,但此块空间是非法空间,访问了还未被清除的无效数据
如何触发清理无效数据呢?只需要一句输出语句即可,如下面代码:
int& add(int a, int b)
{
int sum = a + b;
return sum;
}
int main()
{
int& sum = add(1, 2);
add(3, 4);
cout << endl;
cout << "sum = " << sum << endl;
system("pause");
return 0;
}
执行结果
此时非法空间中的无效数据已经变成了随机值
经典面试题:引用和指针的区别?
从变量角度验证:
int a = 100;
int* pa = &a;
*pa = 10;
int& ra = a;
ra = 10;
从表面上并不能看出啥,我们可以看看底层汇编代码是如何解释这两个东西的?
从汇编代码中我们可以看出:
第一行汇编代码:将 a的地址 放入 eax/edx寄存器中
第二行汇编代码:将 eax/edx寄存器中的数据 放入 pa/ra空间中
第三行汇编代码:将 pa/ra空间中的数据 放入 ecx/eax寄存器中
第四行汇编代码:将 0A这个16进制数据 放入 ecx/eax寄存器中
结论:引用和指针的底层实现方式基本一样
从函数角度验证:
void swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
int a = 1, b = 2;
swap(&a, &b); //调用swap(int* a, int* b)
swap(a, b); //调用swap(int& a, int& b)
分别用引用和指针实现的swap函数也可以看出:引用和指针一模一样哦!