引用本質 探討

前兩次總結了指針用法,發現引用是不是跟指針有某種關係呢?
經過認真總結髮現引用是一種特殊的指針,特殊性如下:
引用是一種變相的指針,不過有更多的限制: 
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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章