在大部分面嚮對象語言如C++、C#、Java,在函數傳參數時除了基礎值類型,對象是經過引用方式傳遞的。shell
然而,在Go語言中,除了map、slice和chan,全部類型(包括struct)都是值傳遞的。框架
那麼,如何在函數外使用函數內處理後的變量呢?只能經過返回新變量嗎?函數
不,可使用指針學習
大部分面嚮對象語言都不多有用到指針的場景了,可是在Go語言中有大量的指針應用場景,要成爲一名合格的Gopher,必須瞭解。指針
每個變量都會分配一塊內存,數據保存在內存中,內存有一個地址,就像門牌號,經過這個地址就能夠找到裏面存儲的數據。code
指針就是保存這個內存地址的變量。對象
在Go語言中,用&
取得變量的地址接口
//爲了說明類型,我採用了顯性的變量定義方法,實際開發中更多的是用「:=」自動獲取類型變量類型 var mystr string = "Hello!" var mystrP *string = &mystr fmt.Println(mystrP)
將以上代碼敲入main函數中,go run
,打印出的內容就是mystr
的內存地址。mystrP
就是mystr
的指針變量。內存
用*
取得指針變量指向的內存地址的值開發
在以前的代碼的後面增長一句代碼:
fmt.Println(*mystrPointer)
go run
運行後,能夠看到打印出 mystr
的值「Hello!」
符號*
也用作定義指針類型的關鍵字。
例如:
var p *int
在其餘OOP語言中,大多數狀況是不須要花太多時間操做指針的,如Java、C#,對象的引用操做都已經交給了虛擬機和框架。而Go常常會用到指針。緣由主要有3個:
interface
時,結構體類型和其指針類型對接口的實現是不一樣的接下來就分別介紹一下,期間會穿插一些簡單的代碼片斷,您能夠建立一個Go文件輸入代碼,運行體驗一下。
Go語言都是值傳遞,例如:
package main import "fmt" func main() { i := 0 f(i) fmt.Println(i) } func f(count int) { fmt.Println(count) count++ }
結果:
0 0
i
在執行先後沒有變化
若是但願被函數調用後,i
的值產生變化,f
函數的參數就應該改成 *int
類型。如:
func main() { i := 0 f(&i) fmt.Println(i) } func f(count *int) { fmt.Println(*count) (*count)++ }
*int
替代 int
,申明參數是一個int類型的指針類型i
,而要傳遞用&
取得i
的地址count
如今是指針了,不能直接打印,須要用*
取出這個指針指向的地址裏保存的值count
的取值+1.main
裏打印i
。能夠看到結果
0 1
i
的值改變了。
Go語言中給結構體定義方法
//定義一個結構體類型 type myStruct struct { Name string } //定義這個結構體的更名方法 func (m myStruct) ChangeName(newName string) { m.Name = newName } func main() { //建立這個結構體變量 mystruct := myStruct{ Name: "zeta", } //調用更名函數 mystruct.ChangeName("Chow") //沒改爲功 fmt.Println(mystruct.Name) }
這樣的方法不會改掉結構體變量內的字段值。 就算是結構體方法,若是不使用指針,方法內仍是傳遞結構體的值。
如今咱們改用指針類型定義結構體方法看看。
只修改 ChangeName
函數,用*myStruct
類型替代myStruct
func (m *myStruct) ChangeName(newName string) { m.Name = newName }
再運行一次,能夠看到打印出來的名字改變了。
當使用指針類型定義方法後,結構體類型的變量調用方法時會自動取得該結構體的指針類型並傳入方法。
最近在某問答平臺回答了一個Gopher的問題,大體內容是問爲何不能用結構體指針類型實現接口方法?
看一下代碼
//定義一個接口 type myInterface interface { ChangeName(string) SayMyName() } //定義一個結構體類型 type myStruct struct { Name string } //定義這個結構體的更名方法 func (m *myStruct) ChangeName(newName string) { m.Name = newName } func (m myStruct) SayMyName() { fmt.Println(m.Name) } //一個使用接口做爲參數的函數 func SetName(s myInterface, name string) { s.ChangeName(name) } func main() { //建立這個結構體變量 mystruct := myStruct{ Name: "zeta", } SetName(mystruct, "Chow") mystruct.SayMyName() }
這段代碼是沒法編譯經過的,會提示
cannot use mystruct (type myStruct) as type myInterface in argument to SetName: myStruct does not implement myInterface (ChangeName method has pointer receiver)
myStruct
類型沒有實現接口方法ChangeName
,也就是說func (m *myStruct) ChangeName(newName string)
並不算實現了接口,由於它是*myStruct
類型實現的,而不是myStruct
。
改一改
在調用SetName時,用&mystruct 替代 mystruct:
SetName(&mystruct, "Chow")
編譯運行,成功。
爲何結構體類型實現的接口該結構體的指針類型也算實現了,而指針類型實現的接口,不算是該結構體實現了接口呢?
緣由是,結構體類型定義的方法能夠被該結構體的指針類型調用;而結構體類型調用該指針類型的方法時是被轉換成指針,不是直接調用。
因此,&mystruct
直接實現了接口定義的ChangeName
和SayMyName
兩個方法,而mystruct
只能實現了SayMyName
,mystruct
調用ChangeName
方法其實轉換成指針類型後調用的,不算實現了接口。
到此Go語言指針類型的應用介紹差很少了。
總結一下:
歡迎你們一塊兒討論、學習Go語言!!