golang中值類型/指針類型的變量區別總結

轉自:https://segmentfault.com/a/1190000012329213segmentfault

值類型的變量和指針類型的變量

先聲明一個結構體:函數

type T struct { Name string } func (t T) M1() { t.Name = "name1" } func (t *T) M2() { t.Name = "name2" }

M1() 的接收者是值類型 T, M2() 的接收者是值類型 *T , 兩個方法內都是改變Name值。測試

下面聲明一個 T 類型的變量,並調用 M1() 和 M2() 。spa

t1 := T{"t1"} fmt.Println("M1調用前:", t1.Name) t1.M1() fmt.Println("M1調用後:", t1.Name) fmt.Println("M2調用前:", t1.Name) t1.M2() fmt.Println("M2調用後:", t1.Name)

輸出結果爲:scala

M1調用前: t1
M1調用後: t1
M2調用前: t1
M2調用後: name2指針

下面猜想一下go會怎麼處理。code

先來約定一下:接收者能夠看做是函數的第一個參數,即這樣的: func M1(t T), func M2(t *T)。 go不是面向對象的語言,因此用那種看起來像面向對象的語法來理解可能有誤差。對象

當調用 t1.M1() 時至關於 M1(t1) ,實參和行參都是類型 T,能夠接受。此時在M1()中的t只是t1的值拷貝,因此M1()的修改影響不到t1。接口

當調用 t1.M2() => M2(t1),這是將 T 類型傳給了 *T 類型,go可能會取 t1 的地址傳進去: M2(&t1)。因此 M2() 的修改能夠影響 t1 。get

  1. 類型的變量這兩個方法都是擁有的。

下面聲明一個 *T 類型的變量,並調用 M1() 和 M2() 。

t2 := &T{"t2"} fmt.Println("M1調用前:", t2.Name) t2.M1() fmt.Println("M1調用後:", t2.Name) fmt.Println("M2調用前:", t2.Name) t2.M2() fmt.Println("M2調用後:", t2.Name)

輸出結果爲:

M1調用前: t2
M1調用後: t2
M2調用前: t2
M2調用後: name2

t2.M1() => M1(t2), t2 是指針類型, 取 t2 的值並拷貝一份傳給 M1。

t2.M2() => M2(t2),都是指針類型,不須要轉換。

*T 類型的變量也是擁有這兩個方法的。

傳給接口會怎樣?

先聲明一個接口

type Intf interface { M1() M2() }

使用:

var t1 T = T{"t1"} t1.M1() t1.M2() var t2 Intf = t1 t2.M1() t2.M2()

報錯:

./main.go:9: cannot use t1 (type T) as type Intf in assignment:

T does not implement Intf (M2 method has pointer receiver) 

var t2 Intf = t1 這一行報錯。

t1 是有 M2() 方法的,可是爲何傳給 t2 時傳不過去呢?

簡單來講,按照接口的理論:傳過去【賦值】的對象必須實現了接口要求的方法,而t1沒有實現M2(),t1的指針實現了M2()。另外和c語言同樣,函數名自己就是指針

當把 var t2 Intf = t1 修改成 var t2 Intf = &t1 時編譯經過,此時 t2 得到的是 t1 的地址, t2.M2() 的修改能夠影響到 t1 了。

若是聲明一個方法 func f(t Intf) , 參數的傳遞和上面的直接賦值是同樣的狀況。

嵌套類型

聲明一個類型 S,將 T 嵌入進去

type S struct { T }

使用下面的例子測試一下:

t1 := T{"t1"} s := S{t1} fmt.Println("M1調用前:", s.Name) s.M1() fmt.Println("M1調用後:", s.Name) fmt.Println("M2調用前:", s.Name) s.M2() fmt.Println("M2調用後:", s.Name) fmt.Println(t1.Name)

輸出:

M1調用前: t1 
M1調用後: t1 
M2調用前: t1 
M2調用後: name2 
t1

將 T 嵌入 S, 那麼 T 擁有的方法和屬性 S 也是擁有的,可是接收者卻不是 S 而是 T。

因此 s.M1() 至關於 M1(t1) 而不是 M1(s)。

最後 t1 的值沒有改變,由於咱們嵌入的是 T 類型,因此 S{t1} 的時候是將 t1 拷貝了一份。

假如咱們將 s 賦值給 Intf 接口會怎麼樣呢?

var intf Intf = s intf.M1() intf.M2()

報錯:

cannot use s (type S) as type Intf in assignment: S does not implement Intf (M2 method has pointer receiver)

仍是 M2() 的問題,由於 s 此時仍是值類型。

var intf Intf = &s 這樣的話編譯經過了,若是在 intf.M2() 中改變了 Name 的值, s.Name 被改變了,可是 t1.Name 依然沒變,由於如今 t1 和 s 已經沒有聯繫了。

下面嵌入 *T 試試:

type S struct { *T }

使用時這樣:

t1 := T{"t1"} s := S{&t1} fmt.Println("M1調用前:", s.Name) s.M1() fmt.Println("M1調用後:", s.Name) fmt.Println("M2調用前:", s.Name) s.M2() fmt.Println("M2調用後:", s.Name) fmt.Println(t1.Name)

M1調用前: t1
M1調用後: t1
M2調用前: t1
M2調用後: name2
name2
唯一的區別是最後 t1 的值變了,由於咱們複製的是指針。

接着賦值給接口試試:

var intf Intf = s     i
    ntf.M1()     
    intf.M2()     
    fmt.Println(s.Name)

編譯沒有報錯。這裏咱們傳遞給 intf 的是值類型而不是指針,爲何能夠經過呢?

拷貝 s 的時候裏面的 T 是指針類型,因此調用 M2() 的時候傳遞進去的是一個指針。

var intf Intf = &s 的效果和上面同樣。

相關文章
相關標籤/搜索