天天看點

C++的那些事:你真的了解引用嗎

說到引用,一般c++的教材中都是這麼定義的:

1,引用就是一個對象的别名。 2,引用不是值不占記憶體空間。 3,引用必須在定義時指派,将變量與引用綁定。

那你有沒有想過,上面的定義正确嗎?編譯器是如何解釋引用的?

這裡先給出引用的本質定義,後面我們再進一步論證。

1,引用實際是通過指針實作的。 2,引用是一個常量指針。 3,引用在記憶體中占4個位元組。 4,在對引用定義時,需要對這個常量指針初始化。

我們從最簡單的變量的定義開始,看編譯器會做哪些事情。

上面語句申請了一塊記憶體空間,占4個位元組,存放了一個int型的變量。記憶體裡放的是42的二進制碼。

彙編代碼向我們表達的意思就是把42寫入以var為位址的内容區域。var有點像我們了解上的指針,隻是編譯器并沒有把它抽象出來,而是讓我們更表象的了解:申請一個變量,它的值為42。

那麼var這個變量名放在哪呢?

我們知道程式如果通路記憶體裡的資料,需要通過位址來進行通路,是以上面的代碼在經過編譯器生成目标代碼時,用存放42的位址了所有的var,是以結論時,目标檔案中不存在var,是以變量名本身是不占記憶體的。

而我們知道,引用是變量的一個别名。那麼,從這很多人會聯想到,引用會不會也隻是一個名字而已,編譯器在生成目标代碼的時候,會用實際位址替換引用呢?

答案并非這樣!

那我們接下來看看,當我們定義一個引用時,發生了什麼:

上面的代碼顯示,當定義一個引用時,編譯器将var的位址賦給了以refvar為位址的一塊記憶體區域。也就是說refvar其實存放的是var的位址。

這讓我們聯想到了指針,那麼我們看看定義一個指針是發生了什麼:

沒錯,沒有任何差别,定義一個引用和一個指針的彙編代碼完全一緻!

相信從上面的分析時,你可能已經相信了,引用實際上就是一個指針。那麼為什麼說引用是一個常量指針呢,在目标代碼裡有什麼展現呢?

這個問題其實要從c++底層機制談起,c++為我們提供的各種存取控制僅僅是在編譯階段給我們的限制,也就是說編譯器確定了你在完成任務之前的正确行為,如果你的行為不正确,那麼編譯器就是給你在編譯時提示錯誤。所謂的const和private等在實際的目标代碼裡根本不存在,是以在程式運作期間隻要你願意,你可以通過記憶體工具修改它的任何一個變量的值。

這也就解釋了為什麼上面的兩段代碼中引用和指針的彙編代碼完全一緻。

c++設計引用,并用常量指針來從編譯器的角度實作它,目标是為了提供比指針更高的安全性,因為常量指針一旦與變量位址綁定将不能更改,這樣降低了指針的危險系數,它提供了一種一對一的指針。

但是你覺得使用引用就安全了嗎?它同樣會有與使用指針一樣的問題

上面這段代碼就很不安全,因為ref引用的記憶體區域不合法。

為了進一步驗證引用與指針在本質上的相同,我們看當引用作為函數參數傳遞時,編譯器的行為:

上面代碼再次證明了,引用與指針的行為完全一緻,隻是編譯器在編譯時對引用作了更嚴格的限制。

因為在在表達式中,使用引用實際上就像使用變量本身一樣,是以直接用sizeof是得不到引用本身的大小的。

我們可以通過定義一個隻含有引用的類來解決這個問題:

是以結論就是引用和指針一樣實際占記憶體空間4個位元組。

參考文章:

繼續閱讀