引用本质 探讨

前两次总结了指针用法,发现引用是不是跟指针有某种关系呢?
经过认真总结发现引用是一种特殊的指针,特殊性如下:
引用是一种变相的指针,不过有更多的限制: 
1,必须定义时初始化. 
2,所引用的对象不能更改 
3,使用时类似于对象. 
  指针使用->调用   引用使用. 跟对象使用方式一样。
下面从下面代码分析出引用和指针调用联系:

// testreference.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
void func (int &ia) {
	ia=100;
}


void func1 (int ia) {
	ia=100;
}

void func2(int *a){
	(*a)=100;
}
int main(int argc, char* argv[])      
{  
	int ix=99;
	func(ix);
	func1(ix);
	func2(&ix);
	cout<<"ix: "<<ix<<endl;
}

在vs 2010 反汇编代码如下:
	func(ix);//引用调用
00411715  lea         eax,[ix]  
00411718  push        eax  
00411719  call        func (411258h)  
0041171E  add         esp,4  
	func1(ix);
00411721  mov         eax,dword ptr [ix]  //把[ix]地址的值赋给eax。
00411724  push        eax  
00411725  call        func1 (41125Dh)  
0041172A  add         esp,4  
	func2(&ix);//指针调用
0041172D  lea         eax,[ix]  
00411730  push        eax  
00411731  call        func2 (411262h)  
00411736  add         esp,4 

可以看到func(&ix) 和 func反汇编代码一样。所以引用和指针调用方式一样。
再看下面程序:
class A {
	double *m_pDb;
};
class B {
	double &m_db;
public:
	B(double db = 0) : m_db(db) {}
};

int main(int argc, char* argv[])      
{  
	cout << "size of class A:"<<sizeof(A) << endl;//4
	cout << "size of class B:"<<sizeof(B) << endl;//4
    	return 0;      
}
运行结果为:
size of class A:4
size of class B:4
Press any key to continue
所以引用本质上也是一个指针。
再来看一个程序:
#include <string>
using namespace std;
int   main() 
{ 
	int   a=100; 
	int*   b   =   &a; 
	int&   c   =   a; 
	
	a++; 
	(*b)++; 
	c++;
	b++;
	return   0; 
} 

反汇编代码:
	int   a=100; 
00411DFE  mov         dword ptr [a],64h  
	int*   b   =   &a; 
00411E05  lea         eax,[a]  
00411E08  mov         dword ptr [b],eax  
	int&   c   =   a; 
00411E0B  lea         eax,[a]  
00411E0E  mov         dword ptr [c],eax  
	
	a++; 
00411E11  mov         eax,dword ptr [a]  
00411E14  add         eax,1  
00411E17  mov         dword ptr [a],eax  
	(*b)++; 
00411E1A  mov         eax,dword ptr [b]  //b为指针变量所以存放的时地址
00411E1D  mov         ecx,dword ptr [eax] //通过刚才得到的地址得到数放到ecx(间接寻址) 
00411E1F  add         ecx,1  
00411E22  mov         edx,dword ptr [b]  
00411E25  mov         dword ptr [edx],ecx  
	c++;
00411E27  mov         eax,dword ptr [c]  
00411E2A  mov         ecx,dword ptr [eax]  
00411E2C  add         ecx,1  
00411E2F  mov         edx,dword ptr [c]  
00411E32  mov         dword ptr [edx],ecx  
	b++;
00411E34  mov         eax,dword ptr [b]  //因为int指针所以下面eax+4代表指针b++.
00411E37  add         eax,4  
00411E3A  mov         dword ptr [b],eax  

可以看到 C++ 与(*b)++ 的反汇编代码是一样的。
int*   b   =   &a; 
int&   c   =   a; 
这两个反汇编代码也是一样。
00411E05  lea         eax,[a]  把a 变量地址赋给 eax  而不是把a 变量的值。
00411E11  mov         eax,dword ptr [a]    这个才是把a 的值赋给eax 。
注意mov 和lea 的区别。

引用的作用(参照 http://topic.csdn.net/t/20040101/13/2624096.html )
1.  反汇编模式下可以看到引用的本质就是指针,在机器代码层次上引用没有另外的区别于指针的实现方式,所以我认为引用更多的是面向抽象层次的使用,即我们在编写C++       代码的时候做一些限制,比如引用不能为NULL。编译器一旦把引用“翻译”成机器代码就和指针没什么区别。
2. 引用具有指针的能力,但是调用引用传递的函数时,可读性却比指针传递好。 
    引用具有传值方式函数调用语法的简单性与可读性,但是威力却比传值方式强!
3. 引用最大的贡献就是消除了指针的不安全,并且给操作浮重载带来巨大的方便 。
4. 对引用要注意一定不能 //char&   rc   =   pc;                 //cannot convert from 'char *' to 'char &',
    其中char g='g';   char   *pc   =   &g;  要char & rc=*pc 才正确。 cout<<rc<<endl; 输出 g
5. 
    引用有个特殊功能,就是当一个临时对象绑定到引用,这个临时对象的生命期,将延长至这个引用的生命期结束时指针却没有这个功能。
可以通过下面代码来理解:
#include <iostream>
#include <string>
using namespace std;
class   A 
{ 
public: 
    int   i; 
    A(){i=50;  cout <<"A() construct"<<endl; } 
    ~A(){i=0;  cout <<"~A()  disconstruct"<<endl;  } 
}; 

void   fun(A*   pa) 
{ 
    cout << "void   fun(A*) " <<endl;   
    cout <<pa-> i <<endl;                   
} 

void   fun(const   A&   ra) 
{ 
    cout << "void   fun(const   A&) " <<endl; 
    cout <<ra.i <<endl; 
} 

int   main() 
{ 
	const   A&   ra=A();     //将一个临时对象帮定到常量引用ra 
	cout<<"const A&   ------------------"<<endl;
	A*   pa=(&A());           //将另临时对象的地址赋给pa,在对临时对象去址后,临时对象就被析构了,生命结束,完成使命  new A()
	cout<<"A*   pa=(&A())   ------------------"<<endl;
	fun(pa);     //输出的是0,临时对象已不存在,这里只是残留的痕迹 
	fun(ra);     //但这里输出的是50,发现没有,那个临时对象还存在,因为没有调用析构函数  //引用的调用方法也不一样。如果去掉 fun(const   A&   ra)会出现cannot convert parameter 1 from 'const class A' to 'class A *'错误。

	A* pap=new A();//这个就不是临时对象了。
	fun(pap);

	A objA();
	cout<<"A objA()   ------------------"<<endl;
    //fun(objA)或者fun(&objA);none of the 2 overloads can convert parameter 1 from type 'class A (__cdecl *)(void)'
} 
输出结果为:
A() construct
const A&   ------------------
A() construct
~A()  disconstruct
A*   pa=(&A())   -------------//这个在~A()disconstruct 后说明A*pa=(&A())是临时对象。
void   fun(A*)
0
void   fun(const   A&)
50//输出50 绑定const   A&   ra=A(); 将延长至这个引用的生命期结束时 指针却没有这个功能。
A() construct
void   fun(A*)
50
A objA()   ------------------
~A()  disconstruct//析构在函数-----后这不是临时对象
Press any key to continue

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值