日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

GO 的方法集

發布時間:2024/8/23 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 GO 的方法集 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

之前在寫 GOdemo 的時候, 寫了這么一段程序(大概意思):

package maintype Test struct { }func (test *Test) print() {println("test fun") }func main() {Test{}.print() }

結果一編譯就報錯了: cannot call pointer method on Test literal

差不多意思是不能調用指針方法. 我一看, 確實, print方法聲明的是指針類型. 這么說我就懂了, 加個取址就 OK 了吧? (&Test{}).print() 這樣就可以調用了.

分析

由此大膽的假設, GO在將方法綁定到結構體的時候, 根據接收的結構體類型不同(值或指針), 會將方法綁定到不同的類型變量上, 也就是說, 指針類型只能調用指針類型的方法, 值類型只能調用值類型的方法.

驗證一下:

package maintype Test struct { }func (test *Test) print() {println("test fun") }func (test Test) print2() {println("test fun 2") }func main() {// 指針類型調用值類型方法(&Test{}).print2()// 指針類型調用指針類型方法(&Test{}).print()// 值類型調用值類型方法Test{}.print2()// 值類型調用指針類型方法Test{}.print() }

結果如何? 只有在使用值類型調用指針類型方法時, 編譯會報錯, 其他情況都 OK.

假設推翻, GO方法的綁定規則應該是(網上搜了搜, 發現這玩意叫 GO 的方法集):

  • 指針類型擁有 值/指針 的方法
  • 值類型只擁有值類型的方法
  • 那么問題來了, 我平常寫的時候, 是這樣的, 就不會報錯呀, 怎么今天突然報錯了? 他們有什么區別么?

    t := Test{} t.print()

    我十分確定, t變量不是指針, 但他就可以調用呀. 查了查發現, 是GO在編譯的時候幫我們隱式的做了取址的操作. 那為什么這里可以幫忙, 上面就不行了呢? 搞不懂.

    在查的時候, 還看到了大概這樣的代碼:

    package main// 定義個測試接口 type ITest interface {print() }type Test struct { }// 實現接口的類 func (test *Test) print() {println("test fun") }func main() {ReceiveTest(Test{}) }// 接收接口的方法 func ReceiveTest(t ITest) {t.print() }

    這個時候, 向方法傳值就會報錯, 有了上面的經驗, 我已經知道了, 值類型沒有綁定print方法, 所以改成傳遞指針就可以了.而且, 在這里, 如果在 ReceiveTest方法中做取址的操作, 也么的用, 只能在向方法傳參的時候做取值操作.

    這里再假設一下, 方法在傳參的時候是傳遞的復制值, 當對值進行復制傳進函數的時候, 儼然已經不是原始的值了, 而是原始值的一個副本, 而對副本再進行取址, 已經是一個新地址了, 自然就沒有綁定其指針函數. 而當參數是指針類型的時候, 對指針類型復制并傳遞, 方法接收到的是一個地址值, 雖然此地址值是一個副本, 但是指向的仍然是原對象.

    OK, 驗證假設(為了保證編譯順利, 只保留了基本內容):

    package mainimport "fmt"type Test struct {Name int }func main() {t := Test{}fmt.Printf("%p\n", &t)ReceiveTest(t) }func ReceiveTest(t Test) {fmt.Printf("%p\n", &t) }

    打印結果不同, 果然不是同一個對象, 而是復制的一個副本. 而對于指針傳遞:

    package mainimport "fmt"type Test struct {Name int }func main() {t := &Test{}fmt.Printf("原始指針變量的地址: %p\n", &t)fmt.Printf("原始指針變量的值: %p\n", t)ReceiveTest(t) }// 接收接口的方法 func ReceiveTest(t *Test) {fmt.Printf("接收指針變量的地址: %p\n", &t)fmt.Printf("接收指針變量的值: %p\n", t) }

    打印結果:

    原始指針變量的地址: 0xc00000e028 原始指針變量的值: 0xc000016068 接收指針變量的地址: 0xc00000e038 接收指針變量的值: 0xc000016068

    結果發現, 指針傳遞保存的對象地址確實會原封不動的傳遞, 但是, 其指針變量卻會創建副本傳進來. 所以可以這樣理解, 不管你是指針類型還是值類型, GO 在函數傳參的時候, 都會對該內容創建一個副本進行傳遞.

    那也就意味著, 如果傳的是一個較大的對象, 進行值的傳遞, 會將整個對象全拷貝一份, 然后傳遞過去, 而傳遞指針只需要拷貝8字節的指針數據就可以了,

    不過如果傳入了指針類型, 就要直面在方法內部可能會對對象進行修改的風險.


    至此, 最開始的疑問已經解答了, 被GO這個t.print(), 調用方法時的隱式轉址蒙蔽了我的雙眼… 雖然這樣在使用的時候就不用特意區分變量類型是值還是地址, 但是有的地方幫我轉了, 有的地方又不管我了, 感覺怪怪的. 再習慣習慣.

    總結

    以上是生活随笔為你收集整理的GO 的方法集的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。