在go語言中,選擇指針類型還是值類型主要取決于結(jié)構(gòu)體大小和是否需要修改原始數(shù)據(jù)。1. 結(jié)構(gòu)體較大或包含大塊數(shù)據(jù)(如數(shù)組、切片、map等)時,優(yōu)先使用指針以減少內(nèi)存開銷和性能損耗;2. 如果函數(shù)需要修改傳入的數(shù)據(jù),必須使用指針,因為go是按值傳遞的;3. 涉及接口實現(xiàn)時,值接收者可以讓結(jié)構(gòu)體和指針都實現(xiàn)接口,更靈活,而大結(jié)構(gòu)體建議用指針接收者避免復(fù)制;4. 小結(jié)構(gòu)體或只讀訪問時可使用值類型,避免副作用且不影響性能。
在go語言中,什么時候用指針類型、什么時候用值類型,其實是一個挺常見的問題。這個問題沒有統(tǒng)一的答案,但可以根據(jù)內(nèi)存使用和性能表現(xiàn)來做一個大致的判斷。
簡單來說:如果你希望函數(shù)修改變量本身,并且這個變量比較大,就該用指針;否則可以考慮用值類型。
下面從幾個實際場景出發(fā),具體講講怎么選。
立即學(xué)習“go語言免費學(xué)習筆記(深入)”;
結(jié)構(gòu)體大小影響性能
Go中的結(jié)構(gòu)體默認是值類型,當你把一個結(jié)構(gòu)體傳給函數(shù)時,默認是復(fù)制整個結(jié)構(gòu)體。如果結(jié)構(gòu)體很大,比如有幾十個字段,或者包含大數(shù)組、嵌套結(jié)構(gòu)體,那么頻繁傳遞這樣的值會帶來額外的內(nèi)存開銷和性能損耗。
舉個例子:
這個User結(jié)構(gòu)體不算小,每次傳參都會復(fù)制一份。這時候就應(yīng)該考慮用指針了:
func updateUser(u *User) { u.Age = 30 }
?建議:結(jié)構(gòu)體字段多或包含大塊數(shù)據(jù)(如數(shù)組、切片、map等)時,優(yōu)先使用指針。如果結(jié)構(gòu)體很小,比如只有兩三個int或string字段,傳值也沒關(guān)系。
是否需要修改原始數(shù)據(jù)
另一個關(guān)鍵點是:你是否需要通過函數(shù)調(diào)用修改原始變量?
如果你寫了一個函數(shù),想讓它修改傳入的對象,那就必須用指針。因為Go是“按值傳遞”的,傳進去的是副本。
例如:
func setName(u User, name string) { u.Name = name }
這樣調(diào)用后,原對象的Name不會變。必須改成:
func setName(u *User, name string) { u.Name = name }
?建議:
- 如果函數(shù)要修改傳入的數(shù)據(jù),用指針。
- 如果只是讀取數(shù)據(jù),可以傳值,避免副作用。
接口實現(xiàn)與方法集
在Go中,方法接收者是指針還是值,會影響它能實現(xiàn)哪些接口。
比如你有一個接口:
type speaker interface { Speak() }
然后定義兩個方法:
func (u User) Speak() { fmt.Println("Hi") } func (u *User) Speak() { fmt.Println("Hi") }
前者是值方法,后者是指針方法。它們的行為略有不同:
- 指針方法:*User 實現(xiàn)了接口,但 User 類型不實現(xiàn)。
- 值方法:User 和 *User 都實現(xiàn)了接口。
所以,如果你希望無論傳值還是指針都能滿足接口要求,最好用值接收者。
?建議:
- 如果你的結(jié)構(gòu)體會被作為接口實現(xiàn)使用,注意接收者的類型選擇。
- 小結(jié)構(gòu)體用值接收者更靈活,大結(jié)構(gòu)體建議用指針接收者避免復(fù)制。
總結(jié)一下
- 結(jié)構(gòu)體大 → 用指針
- 需要修改原對象 → 用指針
- 只讀訪問或結(jié)構(gòu)體小 → 可以用值
- 涉及接口實現(xiàn) → 看接收者類型
基本上就這些,看似簡單,但細節(jié)容易忽略。