先了解下變量&地址&指針的關係:點擊跳轉去看數組
咱們在前面屢次提到過指針及指針類型。例如,*Person
是Person
的指針類型。又例如,表達式&p
的求值結果是p
的指針。方法的接收者類型的不一樣會給方法的功能帶來什麼影響?該方法所屬的類型又會所以發生哪些潛移默化的改變?如今,咱們就來解答第一個問題。至於第二個問題,我會在下一小節予以解答。ui
指針操做涉及到兩個操做符——&
和*
。這兩個操做符均有多個用途。可是當它們做爲地址操做符出現時,前者的做用是取址,然後者的做用是取值。更通俗地講,當地址操做符&
被應用到一個值上時會取出指向該值的指針值,而當地址操做符*
被應用到一個指針值上時會取出該指針指向的那個值。它們能夠被視爲相反的操做。
除此以外,當*
出如今一個類型以前(如*Person
和*[3]string
)時就不能被看作是操做符了,而應該被視爲一個符號。如此組合而成的標識符所表達的含義是做爲第二部分的那個類型的指針類型。咱們也能夠把其中的第二部分所表明的類型稱爲基底類型。例如,*[3]string
是數組類型[3]string
的指針類型,而[3]string
是*[3]string
的基底類型。
好了,咱們如今回過頭去再看結構體類型Person
。它及其兩個方法的完整聲明以下:spa
type Person struct { Name string Gender string Age uint8 Address string } func (person *Person) Grow() { person.Age++ } func (person *Person) Move(newAddress string) string { old := person.Address person.Address = newAddress return old }
注意,Person
的兩個方法Grow
和Move
的接收者類型都是*Person
,而不是Person
。只要一個方法的接收者類型是其所屬類型的指針類型而不是該類型自己,那麼我就能夠稱該方法爲一個指針方法。上面的Grow
方法和Move
方法都是Person
類型的指針方法。
相對的,若是一個方法的接收者類型就是其所屬的類型自己,那麼咱們就能夠把它叫作值方法。咱們只要微調一下Grow
方法的接收者類型就能夠把它從指針方法變爲值方法:.net
func (person Person) Grow() { person.Age++ }
那指針方法和值方法到底有什麼區別呢?咱們在保留上述修改的前提下編寫以下代碼:指針
p := Person{"Robert", "Male", 33, "Beijing"} p.Grow() fmt.Printf("%v\n", p)
這段代碼被執行後,標準輸出會打印出什麼內容呢?直覺上,34
會被打印出來,可是被打印出來的倒是33
。這是怎麼回事呢?Grow
方法的功能失效了?!
解答這個問題須要引出一條定論:方法的接收者標識符所表明的是該方法當前所屬的那個值的一個副本,而不是該值自己。例如,在上述代碼中,Person
類型的Grow
方法的接收者標識符person
表明的是p
的值的一個拷貝,而不是p
的值。咱們在調用Grow
方法的時候,Go語言會將p
的值複製一份並將其做爲這次調用的當前值。正由於如此,Grow
方法中的person.Age++
語句的執行會使這個副本的Age
字段的值變爲34
,而p
的Age
字段的值卻依然是33
。這就是問題所在。
只要咱們把Grow
變回指針方法就能夠解決這個問題。緣由是,這時的person
表明的是p
的值的指針的副本。指針的副本仍會指向p
的值。另外,之因此選擇表達式person.Age
成立,是由於若是Go語言發現person
是指針而且指向的那個值有Age
字段,那麼就會把該表達式視爲(*person).Age
。其實,這時的person.Age
正是(*person).Age
的速記法。code
我的最後結果:點擊跳轉去看blog