c++ 引用的分析

在一般教材里面,我们会说引用是变量的别名,另外在 c++ primer 5里面说到引用的时候,说引用不是对象,不能对它进行取地址。但是我们来看看下面代码的分析:

 1 #include <iostream>
 2
 3 int main(int argc, char* argv[])
 4 {
 5     int i = 90;
 6     int &qi = i;
 7     int *pqi = &(&qi);
 8     int *pi = &i;
 9
10     return 0;
11 }

汇编结果:

 1 #include <iostream>
 2
 3 int main(int argc, char* argv[])
 4 {
 5 00BC4350  push        ebp
 6 00BC4351  mov         ebp,esp
 7 00BC4353  sub         esp,0F0h
 8 00BC4359  push        ebx
 9 00BC435A  push        esi
10 00BC435B  push        edi
11 00BC435C  lea         edi,[ebp-0F0h]
12 00BC4362  mov         ecx,3Ch
13 00BC4367  mov         eax,0CCCCCCCCh
14 00BC436C  rep stos    dword ptr es:[edi]
15     int i = 90;
16 00BC436E  mov         dword ptr [i],5Ah  // wc: 将90 保存到 i 的内存单元
17     int &qi = i;
18 00BC4375  lea         eax,[i]
19 00BC4378  mov         dword ptr [qi],eax  // wc: 将 i 的地址保存到 qi 的内存单元,这里可以说明引用也是有自己的内存空间的,
20                                           // wc: 它用来保存所绑定的对象的地址
21     int *pqi = &qi;
22 00BC437B  mov         eax,dword ptr [qi]
23 00BC437E  mov         dword ptr [pqi],eax  // wc: 这里是将引用取地址给一个指针,我们可以看到,实际上是将 qi 内存单元里的值保存到 pqi 的内存单元,
24                                            // wc: 那么也就说,对一个引用取地址实际上是对其取值,取出它保存的所绑定对象的地址
25                                            // wc: 这也就说明了为什么对一个引用取地址得到的是指向其所绑定对象
26     int *pi = &i;
27 00BC4381  lea         eax,[i]
28 00BC4384  mov         dword ptr [pi],eax  // wc: 将 i 的地址保存到 pi 的内存单元,这个是一般的对一个对象取地址
29
30     return 0;
31 00BC4387  xor         eax,eax
32 }

这么来说,引用的内部实现可能是常量指针,这样引用就是一个可以被自动解引用的指针。

表达式   int &qi = i; 将会被编译器转化成 int *const qi = &i; 而引用之所以要初始化是因为 const 类型变量必须初始化,这个指针也必须指向。我们还可以以另外一个程序来证明:

 1  #include <iostream>
 2  using namespace std;
 3
 4  struct Tq {
 5     int &i;
 6     double &j;
 7  };
 8
 9  int main(int argc, char* argv[])
10  {
11     ┊int *ptr;
12     ┊cout << "size of pointer: " << sizeof(ptr) << endl;
13     ┊cout << "size of Tq: " << sizeof(Tq) << endl;
14
15     ┊   return 0;
16
17  } 

运行结果:

size of pointer: 8

size of Tq: 16

可以看出,两个引用其实就是两个指针。而不是像书上说的引用不是对象。

以上的结果都说明,引用其实就是一个指针,基于引用一定要初始化和不能改变绑定,引用的实现应该是 const pointer,它是一个被自动解引用的指针,它的一系列引用操作由编译器来保证。

时间: 2024-10-18 19:17:54

c++ 引用的分析的相关文章

Python模块的交叉引用问题分析

实际项目中遇到python模块相互引用问题,查资料,说是通过import局部导入的方式可以避免错误,资料如附录所述. 但更改后测试还是会出错,很疑惑!? 如果哪位读者有好的解决方法,敬请留言说明,谢谢. 所以,最好的方法是不进行交叉引用,如果需要就单独分一个模块出来. 附录:Python模块的交叉引用问题解读:How can I have modules that mutually import each other? 有下面两个文件相互引用,Python解释器报错. foo.py: from

python的引用计数分析(二)

python所有对象引用计数被减少1的情况: 一.对象的别名被赋予新的对象; a = 23345455 # 增加了一个引用 b = a # 增加了一个引用 print(sys.getrefcount(a)) b = 1.4 # 减少了一个23345455整数的引用 print(sys.getrefcount(a)) 结果:3:2 二.对象的别名被显式销毁; a = 23345455 # 增加了一个引用 b = a # 增加了一个引用 list = [a, b] # 增加了2个引用 del a p

const 引用的分析

const 引用: 在初始化常量引用时,允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可.尤其,允许为一个常量引用绑定非常量的对象.字面值,甚至是一个表达式.我们来看 const 引用的分析: #include <iostream> int main(int argc, char* argv[]) { const int &i = 12; return 0; } 该代码的汇编代码如下: int main(int argc, char* argv[]) { 00964

OC对象之旅 weak弱引用实现分析

Runtime源码分析带你了解OC实现过程.其中参考了大量的大神的代码以及文献里面也有个人的见解欢迎拍砖欢迎交流. 两种常见使用场景 /// weak属性@interface XX : [email protected](nonatomic,weak) Type* weakPtr;@end/// 代码块中使用{    /// 使用__weak     __weak Type* weakPtr = [[SomeObject alloc] init]; } 根据调试信息发现两者的区别是 第一种进入到

引用传递分析/没学明白(不用太纠结垃圾内存的问题

类本身属于引用数据类型,既然是引用数据类型,那么牵扯到内存的引用传递,引用传递的本质:同一块堆内存空间可以 被不同的栈内存所指向,所有的引用传递的本质是一场堆内存的游戏 引用内存的过程中会产生垃圾,对垃圾的产生进行分析:所有没有栈内存指向的堆内存空间,就叫垃圾空间 所有的垃圾将被GC(Garbage Collector,垃圾收集器)不定期进行回收,并且释放空间,如果垃圾过多,一定 影响GC的处理性能,从而影响程序的运行速度. 一个栈内存只能保存一个堆内存的地址数据,如果发生更改,则之前地址数据将

Java内部类持有外部类的引用详细分析与解决方案

在Java中内部类的定义与使用一般为成员内部类与匿名内部类,他们的对象都会隐式持有外部类对象的引用,影响外部类对象的回收. GC只会回收没有被引用或者根集不可到达的对象(取决于GC算法),内部类在生命周期内始终持有外部类的对象的引用,造成外部类的对象始终不满足GC的回收条件,反映在内存上就是内存泄露.(如,Android中Activity的内存泄露) 解决方案为 1.将内部类定义为static 2.用static的变量引用匿名内部类的实例 测试代码 class Outer { class Inn

java中子类与父类中隐含的this引用的分析

/* 看一下下面的程序,看是否你的答案和运行的答案是否一致! */ class Parent{ public int x; public Parent p; public Parent(){} public Parent(int x){ this.x=x; p=this; } public void f(){ System.out.println("Parent::f()"); } public void g(){ System.out.println("Parent::g(

objective-c启用ARC时的内存管理 (循环引用)

PDF版下载:http://download.csdn.net/detail/cuibo1123/7443125          在Objective-C中,内存的引用计数一直是一个让人比较头疼的问题.尤其是当引用计数涉及到arc.blocks等等的时候.似乎ARC的出现只是让我们解放了双手,由于底层实现依然依赖引用计数,所以开启ARC后,只有对引用计数机制更加了解,才能避免Cycle Retain.Crash等问题的出现. 但是由于使用ARC可以显著提高编码效率,所以建议尽量启用arc,本文

C++中关于[]静态数组和new分配的动态数组的区别分析

这篇文章主要介绍了C++中关于[]静态数组和new分配的动态数组的区别分析,很重要的概念,需要的朋友可以参考下 本文以实例分析了C++语言中关于[]静态数组和new分配的动态数组的区别,可以帮助大家加深对C++语言数组的理解.具体区别如下: 一.对静态数组名进行sizeof运算时,结果是整个数组占用空间的大小:因此可以用sizeof(数组名)/sizeof(*数组名)来获取数组的长度.int a[5]; 则sizeof(a)=20,sizeof(*a)=4.因为整个数组共占20字节,首个元素(i