前言
在C的世界里要写一个函数对两个数进行交换,需要通过指针,进行传址。但在C++的世界里新增了一种新的类型来完成上述操作,这种类型就是引用。
相关的代码如下:
#include <iostream>
using namespace std;
void mySwap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10, b = 20;
cout << "a=" << a << " b=" << b << endl;
mySwap(a, b);
cout << "a=" << a << " b=" << b << endl;
return 0;
}
可以看到我们直接在调用mySwap函数时,用法跟传值一样,将实参a,b传递给了mySwap函数,不需要类似传址一样,加上取地址符&。
定义
引用为对象起了另外一个名字,引用并非对象。如何定义一个引用类型呢?我们只需要在变量前面
加上&。
使用引用需要注意以下几点:
1、定义引用时需要跟一个具体的对象绑定在一起,什么是具体的对象,我们可以简单的理解成为,在内存中有一块真正的内存去存储这个对象。因此引用必须初始化。如:
int &refVal; //错误,无法令引用重新绑定到另外一个对象,需要初始化
---------------------------------------
int a = 10;
int &refVal = a; //正确
---------------------------------------
int &refVal = 20; //错误,并不是一个对象
2、引用本身不是对象,因此引用不占用内存空间(至少在编译器给我们看到的现象是这样的)
3、引用的类型必须要和与之绑定的对象类型严格匹配
- 常量引用除外
- 基类引用派生类对象除外
进一步探究引用底层实现
示例代码如下:
#include <iostream>
using namespace std;
int main() {
int a = 10;
int &refVal = a;
double dval = 3.456;
const int &ri = dval;
return 0;
}
对上述代码进行编译,并使用gdb进行调试(关于gdb的一些使用操作可以参考http://t.csdnimg.cn/vXsEn):
我们从7c1处这条汇编指令开始分析,将10存放在了栈空间$rbp-0x28处,即变量a的地址是$rbp-0x28(ffdf68)
继续单步调试代码:
可以看到int &refVal = a;这条语句的汇编指令是7c8和7cc两条。这两条指令含义是将变量a的地址存放在了栈空间$rbp-0x20处:
可以看到编译器其实在底层实现上,给引用refVal分配了内存,内存地址是ffdf70,在底层实现中引用类似于指针,但是最终我们使用时是看不到ffdf70的。访问refVal就是访问变量a的地址,编译器屏蔽了底层实现的细节!!!
继续单步调试:
double dval = 3.456;
const int &ri = dval;
上述两句代码的汇编语句是从7d0~7ed。
相关汇编的含义是:
movsd 0x128(%rip),%xmm0 从内存中以当前指令指针加上0x128字节的地址开始的位置读取一个双精度浮点数,并将其存放到%xmm0
寄存器中。
cvttsd2si %xmm0,%eax
是一条将双精度浮点数(Double-Precision Floating-Point)转换为32位整数的汇编指令,即将3.456转化为3
mov %eax,-0x24(%rbp) 将3存放在栈空间$rbp-0x24处,这个3是个临时量对象。
lea -0x24(%rbp),%rax
mov %rax,-0x10(%rbp) 将ffdf6c的地址存放到栈空间$rbp-0x10处
double dval = 3.456;
const int &ri = dval;
//编译器实际将上述代码做了如下形式处理:
const int temp = dval;
const int &ri = temp;
temp的值是3,地址是ffdf6c ,ri绑定的是temp这个临时量,ri类似指针效果,前文已经叙述。