值類型的變量和指針類型的變量
先聲明一個結構體:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
M1() 的接收者是值類型 T, M2() 的接收者是值類型 *T , 兩個方法內都是改變Name值。
下面聲明一個 T
類型的變量,並調用 M1()
和 M2()
。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
輸出結果為:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
下面猜測一下go會怎么處理。
先來約定一下:接收者可以看作是函數的第一個參數,即這樣的: 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 。
T 類型的變量這兩個方法都是擁有的。
下面聲明一個 *T
類型的變量,並調用 M1()
和 M2()
。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
輸出結果為:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
t2.M1()
=> M1(t2)
, t2 是指針類型, 取 t2 的值並拷貝一份傳給 M1。
t2.M2()
=> M2(t2)
,都是指針類型,不需要轉換。
*T 類型的變量也是擁有這兩個方法的。
先聲明一個結構體:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
M1() 的接收者是值類型 T, M2() 的接收者是值類型 *T , 兩個方法內都是改變Name值。
下面聲明一個 T
類型的變量,並調用 M1()
和 M2()
。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
輸出結果為:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
下面猜測一下go會怎么處理。
先來約定一下:接收者可以看作是函數的第一個參數,即這樣的: 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 。
T 類型的變量這兩個方法都是擁有的。
下面聲明一個 *T
類型的變量,並調用 M1()
和 M2()
。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
輸出結果為:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
t2.M1()
=> M1(t2)
, t2 是指針類型, 取 t2 的值並拷貝一份傳給 M1。
t2.M2()
=> M2(t2)
,都是指針類型,不需要轉換。
*T 類型的變量也是擁有這兩個方法的。
傳給接口會怎樣?
先聲明一個接口
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
使用:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
報錯:
- 1
- 2
- 1
- 2
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)
, 參數的傳遞和上面的直接賦值是一樣的情況。
先聲明一個接口
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
使用:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
報錯:
- 1
- 2
- 1
- 2
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 嵌入進去
- 1
- 2
- 3
- 1
- 2
- 3
使用下面的例子測試一下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
輸出:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
將 T 嵌入 S, 那么 T 擁有的方法和屬性 S 也是擁有的,但是接收者卻不是 S 而是 T。
所以 s.M1()
相當於 M1(t1)
而不是 M1(s)
。
最后 t1 的值沒有改變,因為我們嵌入的是 T 類型,所以 S{t1}
的時候是將 t1 拷貝了一份。
假如我們將 s 賦值給 Intf 接口會怎么樣呢?
- 1
- 2
- 3
- 1
- 2
- 3
報錯:
- 1
- 2
- 1
- 2
還是 M2() 的問題,因為 s 此時還是值類型。
var intf Intf = &s
這樣的話編譯通過了,如果在 intf.M2()
中改變了 Name 的值, s.Name
被改變了,但是 t1.Name
依然沒變,因為現在 t1 和 s 已經沒有聯系了。
下面嵌入 *T 試試:
- 1
- 2
- 3
- 1
- 2
- 3
使用時這樣:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
惟一的區別是最后 t1 的值變了,因為我們復制的是指針。
接着賦值給接口試試:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
編譯沒有報錯。這里我們傳遞給 intf 的是值類型而不是指針,為什么可以通過呢?
拷貝 s 的時候里面的 T 是指針類型,所以調用 M2() 的時候傳遞進去的是一個指針。
var intf Intf = &s
的效果和上面一樣。