const 引用:
在初始化常量引用时,允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可。尤其,允许为一个常量引用绑定非常量的对象、字面值,甚至是一个表达式。我们来看 const 引用的分析:
#include <iostream> int main(int argc, char* argv[]) { const int &i = 12; return 0; }
该代码的汇编代码如下:
int main(int argc, char* argv[]) { 00964C80 push ebp 00964C81 mov ebp,esp 00964C83 sub esp,0D8h 00964C89 push ebx 00964C8A push esi 00964C8B push edi 00964C8C lea edi,[ebp-0D8h] 00964C92 mov ecx,36h 00964C97 mov eax,0CCCCCCCCh 00964C9C rep stos dword ptr es:[edi] const int &i = 12; 00964C9E mov dword ptr [ebp-14h],0Ch 00964CA5 lea eax,[ebp-14h] 00964CA8 mov dword ptr [i],eax return 0; 00964CAB xor eax,eax }
我们可以看到,const 引用绑定一个12的时候,相当于有如下的步骤:
int temp = 12;
const int &i = temp;
我们上面分析过,引用实质上是一个指针,绑定一个对象就是保存对象的地址,那么一个12是没有地址的,所以需要一个临时变量。当然如果那个常量本身有地址,那么久直接将其地址保存到引用的内存空间。
下面考虑一个常量引用绑定到另一种类型时发生了什么:
int main(int argc, char* argv[]) { double num = 23.2; const int &i = num; return 0; }
汇编结果:
double num = 23.2; 000E436E movsd xmm0,mmword ptr ds:[0ECD80h] double num = 23.2; 000E4376 movsd mmword ptr [num],xmm0 const int &i = num; 000E437B cvttsd2si eax,mmword ptr [num] 000E4380 mov dword ptr [ebp-24h],eax 000E4383 lea ecx,[ebp-24h] 000E4386 mov dword ptr [i],ecx return 0; 000E4389 xor eax,eax }
我们可以看到,这里也是生成了一个临时变量,步骤相当于如下:
const int temp = num;
const int &i = temp;
所以,如果 i 不是常量引用,那么就应该允许对 i 所绑定的对象进行修改,但是 temp 是一个临时变量,明显是一个右值,不合法。
当然,如果是这样的代码
int main(int argc, char* argv[]) { const int num = 23; const int &i = num; return 0; }
那么就不需要一个中间变量。
于是这里就出现了一个很有趣的问题,当一个 const 引用绑定一个非常量对象的时候,其行为可能是不同的,比如如下:
int main(int argc, char* argv[]) { double num = 23.9; const int &i = num; num = 54.9; cout << i << endl; return 0; }
这份代码的结果是:23
而下面这份:
int main(int argc, char* argv[]) { int num = 23; const int &i = num; num = 54; cout << i << endl; return 0; }
结果是 54. 就是因为上面那份生成了一个中间变量的原因。
所以在使用 const 引用绑定非 const 变量的时候要注意这个问题。
时间: 2024-10-23 04:46:42