Reference 型別

已經是個蠻廣泛使用的概念

最早什麼時候開始不清楚

至少C++這個有點前期的物件導向語言中就有出現

而且還是特別獨立出來的一個型別

但是因為C++與C相容的特性

這讓很多接觸C/C++的人完全搞不清楚指標 (pointer) 和 reference型別有啥差別

更甭提要使用。(因為光看到指標都已經暈頭轉向)

 

比較簡單的說法

Reference 讓你感覺好像在使用一個變數一樣

而且還兼具指標的特性

嗯.......

這句話有講跟沒講是一樣的

 

Refernce ?

Reference是啥? 記憶體位址

我想有學過一點 C 的都講得出來,而且應該都會這樣說

但是函式呼叫的時候,丟一個陣列 (array) 是傳遞參考 (pass by reference)

丟一個指標也是 pass by reference,指標是 reference ??

搞不懂了吧? 

先撇開物理實體上的牽扯

Reference個人認為是一個幫助定位的東西

名稱是一個reference,數字座標也是一個 reference

電腦中當然資料是基本上會到記憶體上儲存的

所以記憶體的位址是一個最終的 reference

變數是一個容器,這個容器放在哪裡,就要靠這個reference來定位

陣列是一個大集合的資料

C語言可能為了確保指派 (assign) 的時候速度

也可能因為陣列有需要靠索引 (index) 定位簡單計算位移的特性

和指標可達成的功能相近所以將陣列的指派定為 pass by reference

(後者比較有可能,因為 structure的指派是pass by value)

簡單在說明一下,所謂 pass by xxx,就是把 xxx 複製一份給某個容器

所以 xxx 自然需要有一個東西能夠儲存他,所以指標就是一個用來儲存 reference的容器

再次強調,指標自己本身也是一個容器,既然是一個容器勢必也有自己的 reference

但是這個容器儲存的是別人的 reference

因此當傳一個 array的時候,是傳 array的 reference沒錯

但是當傳遞一個指標,對這個指標而言,他是 pass by value的

而對 reference是這個 value 的區塊而言,才是 pass by reference,這點要非常清楚

 

指標的悲劇

既然指標是一個容器,那麼不能對容器內容做任何運算也有點說不過去

可能是組合語言時代傳承下來的應用方式,可能是為了一個自由度

C語言提供對指標內容的運算,當然 +1減 -1實際加減的記憶體位移

是會根據指標所指向的型別做調整

可是指標是要 dereference回去存取用的

那個時候物件導向的觀念還沒有出來

因此對於型別之間的關聯並沒有那麼強烈

同一個指標所儲存的 reference,可以解釋成不同的指向型別

一個 reference可以被看成指向各種大小不同的型別去作使用

當然這提供記憶體區塊共用一個很大的自由 (畢竟以前記憶體沒現在那麼大)

但是對於初學者完全是個悲劇

首先為了提供記憶體區塊共用,以及提供做記憶體位址運算的功能

使得程式完全沒有辦法自動對指標的內容做範圍控管

常常一不小心就寫到不該碰到的位置上去了

第二因為型別之間沒有關聯,而且又是因為記憶體共用的自由度

編譯器 (compiler) 也沒辦法阻止你要怎麼去對一個 reference做什麼解釋

頂多是出個警告說有危險而已,但是這也僅僅是在指標內容相互指派的時候

況且還有 cast機制和 void * 型別這兩個大魔王讓警告連想出都出不來

光這幾個因素就讓搞不清楚狀況的初學者頭腦混亂

寫到不該寫的地方還不知道自己是怎麼死的

嘿嘿,所以地震就會變成下雨造成的

 

Reference型別

為了避免使用者直接碰到記憶體位址和記憶體區塊解讀

就讓 reference 被完全掌握在系統 (編譯器或 runtime) 最好啦

使用者連管都不需要管

再說了,面對資源相對過去豐富的電腦環境來說

記憶體共用幾乎完全沒有必要,何必再讓開發人員接觸那麼複雜的東西?

(註:但是記憶體區塊的解釋方式,是物件導向繼承和多型 (polymorphism) 很重大的基礎)

那麼指標和reference型別的概念到底差在哪裡?

嗯...技術概念大概就是「持有一個reference」和「共享一個 reference」

如果有碰 Unix/Linux使用 ext檔案系統的話

清楚硬連結 (hard link) 和軟連結 (soft link) 是什麼鬼的話,應該馬上就瞭了

沒接觸過的話大概這樣解釋吧

當你需要透過 A 去 X 時

當 A 是指標時,你必須先問政府機關 A 在哪,找到 A 後,A 再告訴你要怎麼過去 X 那

當 A 是 reference型別時,問政府機關 A是啥,政府機關就告訴你 A 就是 X

即 reference 型別也包含了別名的概念

 

有了 Reference型別後?

因為 reference型別出現,記憶體位址這東西基本上可說是完全被隱藏了

這就可以讓某些部分的彈性大一些

當然這不是對程式開發而言,因為他是少了一項控制因素

而對轉譯器 (編譯器或直譯器 (interpreter) ) 來說是

因為記憶體位置範圍相關的東西只有自己能動

那麼當然就能對這方面做更完全的控管和限制

這會讓程式開發更安全 (但是會比較綁手綁腳)

並且 C++是物件導向的程式語言,增加型別關聯性

只是 C++還保留 pointer的使用 (為了和舊有 C 程式相容)

所以習慣寫 C 的人寫起 C++還是不會比較安全

況且 C++本身還有一個叫 static_cast 的運算子

這基本上跟 void *一樣讓編譯器連想出警告都沒辦法出

Java這方面就做得比較完整了,他的 cast (相當於 C++的 dynamic_cast)

是會做型別的關連檢查的,但是這還有up cast和 down cast兩種

down cast就是將基礎類別 (base class) 轉換為延伸類別 (derived class)

derived class原則上使用到的記憶體區塊一定比base大,這是唯一危險的地方

但至少有大部分亂轉的地方可以在第一時間就發生錯誤而防掉

而且 Java還有 virtual machine 這層基本上就是直譯器的中介層

還可以在執行期間對記憶體的界限做更進一步的控管

 

怎麼證明 reference型別是共享reference

只要打印 reference型別變數的位址即可

感謝 C++ 對 C 語言的相容性吧

arrow
arrow
    全站熱搜

    wylokgo101 發表在 痞客邦 留言(0) 人氣()