說到引用,一般C++的教材中都是這麼定義的:
1,引用就是一個對象的別名。
2,引用不是值不占內存空間。
3,引用必須在定義時賦值,將變量與引用綁定。
那你有沒有想過,上面的定義正確嗎?編譯器是如何解釋引用的?
這裡先給出引用的本質定義,後面我們再進一步論證。
1,引用實際是通過指針實現的。
2,引用是一個常量指針。
3,引用在內存中占4個字節。
4,在對引用定義時,需要對這個常量指針初始化。
我們從最簡單的變量的定義開始,看編譯器會做哪些事情。
var = dword ptr [var],2Ah // 對應匯編代碼
上面語句申請了一塊內存空間,占4個字節,存放了一個int型的變量。內存裡放的是42的二進制碼。
匯編代碼向我們表達的意思就是把42寫入以var為地址的內容區域。var有點像我們理解上的指針,只是編譯器並沒有把它抽象出來,而是讓我們更表象的理解:申請一個變量,它的值為42。
那麼var這個變量名放在哪呢?
我們知道程序如果訪問內存裡的數據,需要通過地址來進行訪問,所以上面的代碼在經過編譯器生成目標代碼時,用存放42的地址了所有的var,所以結論時,目標文件中不存在var,所以變量名本身是不占內存的。
而我們知道,引用是變量的一個別名。那麼,從這很多人會聯想到,引用會不會也只是一個名字而已,編譯器在生成目標代碼的時候,會用實際地址替換引用呢?
答案並非這樣!
那我們接下來看看,當我們定義一個引用時,發生了什麼:
var = & refVar = var dword ptr [refVar],eax
上面的代碼顯示,當定義一個引用時,編譯器將var的地址賦給了以refVar為地址的一塊內存區域。也就是說refVar其實存放的是var的地址。
這讓我們聯想到了指針,那麼我們看看定義一個指針是發生了什麼:
var = * ptrVar = &var dword ptr [ptrVar],eax
沒錯,沒有任何差別,定義一個引用和一個指針的匯編代碼完全一致!
相信從上面的分析時,你可能已經相信了,引用實際上就是一個指針。那麼為什麼說引用是一個常量指針呢,在目標代碼裡有什麼體現呢?
這個問題其實要從C++底層機制談起,C++為我們提供的各種存取控制僅僅是在編譯階段給我們的限制,也就是說編譯器確保了你在完成任務之前的正確行為,如果你的行為不正確,那麼編譯器就是給你在編譯時提示錯誤。所謂的const和private等在實際的目標代碼裡根本不存在,所以在程序運行期間只要你願意,你可以通過內存工具修改它的任何一個變量的值。
這也就解釋了為什麼上面的兩段代碼中引用和指針的匯編代碼完全一致。
C++設計引用,並用常量指針來從編譯器的角度實現它,目標是為了提供比指針更高的安全性,因為常量指針一旦與變量地址綁定將不能更改,這樣降低了指針的危險系數,它提供了一種一對一的指針。
但是你覺得使用引用就安全了嗎?它同樣會有與使用指針一樣的問題
* = ( & = * delete = ;
上面這段代碼就很不安全,因為ref引用的內存區域不合法。
為了進一步驗證引用與指針在本質上的相同,我們看當引用作為函數參數傳遞時,編譯器的行為:
void Swap(& v1, & v2) void Swap(* v1, * v2) var1 = dword ptr [var1], var2 = dword ptr [var2], Swap(var1,var2) esp, Swap(&var1, &var2) esp,
上面代碼再次證明了,引用與指針的行為完全一致,只是編譯器在編譯時對引用作了更嚴格的限制。
因為在在表達式中,使用引用實際上就像使用變量本身一樣,所以直接用sizeof是得不到引用本身的大小的。
= & = << << endl; cout << << endl;
我們可以通過定義一個只含有引用的類來解決這個問題:
& refClass( = ) :( cout << refClass << endl;
所以結論就是引用和指針一樣實際占內存空間4個字節。
參考文章:http://www.cnblogs.com/rollenholt/articles/1907408.html