golang筆記之方法(二)基于指針對象的方法

當(dāng)調(diào)用一個函數(shù)時,會對其每一個參數(shù)值進(jìn)行拷貝,如果一個函數(shù)需要更新一個變量,或者函數(shù)的其中一個參數(shù)實在太大我們希望能夠避免進(jìn)行這種默認(rèn)的拷貝,這種情況下我們就需要用到指針了。對應(yīng)到我們這里用來更新接收器的對象的方法,當(dāng)這個接受者變量本身比較大時,我們就可以用其指針而不是對象來聲明方法。

如下:


func (p *Point) ScaleBy(factor float64) {
    p.X *= factor
    p.Y *= factor
}

總結(jié)為兩點

  1. 不管你的method的receiver是指針類型還是非指針類型,都是可以通過指針/非指針類型進(jìn)行調(diào)用的,編譯器會幫你做類型轉(zhuǎn)換。
  2. 在聲明一個method的receiver該是指針還是非指針類型時,你需要考慮兩方面的因素,第一方面是這個對象本身是不是特別大,如果聲明為非指針變量時,調(diào)用會產(chǎn)生一次拷貝;第二方面是如果你用指針類型作為receiver,那么你一定要注意,這種指針類型指向的始終是一塊內(nèi)存地址,就算你對其進(jìn)行了拷貝。熟悉C或者C++的人這里應(yīng)該很快能明白。

Pointer receivers

You can declare methods with pointer receivers.

This means the receiver type has the literal syntax *T for some type T. (Also, T cannot itself be a pointer such as *int.)

For example, the Scale method here is defined on *Vertex.

Methods with pointer receivers can modify the value to which the receiver points (as Scale does here). Since methods often need to modify their receiver, pointer receivers are more common than value receivers.

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}

func main() {
    v := Vertex{3, 4}
    v.Scale(10)
    fmt.Println(v.Abs())//結(jié)果為50。當(dāng)func (v *Vertex) Scale(f float64)變成func (v Vertex) Scale(f float64),結(jié)果為5,因為Scale沒有改變Vertex結(jié)構(gòu)體中的字段值。
}

Try removing the * from the declaration of the Scale function on line 16 and observe how the program's behavior changes.

With a value receiver, the Scale method operates on a copy of the original Vertex value. (This is the same behavior as for any other function argument.) The Scale method must have a pointer receiver to change the Vertex value declared in the main function.

Nil也是一個合法的接收器類型

就像一些函數(shù)允許nil指針作為參數(shù)一樣,方法理論上也可以用nil指針作為其接收器,尤其當(dāng)nil對于對象來說是合法的零值時,比如map或者slice。在下面的簡單int鏈表的例子里,nil代表的是空鏈表:

// An IntList is a linked list of integers.
// A nil *IntList represents the empty list.
type IntList struct {
    Value int
    Tail  *IntList
}
// Sum returns the sum of the list elements.
func (list *IntList) Sum() int {
    if list == nil {
        return 0
    }
    return list.Value + list.Tail.Sum()
}

當(dāng)你定義一個允許nil作為接收器值的方法的類型時,在類型前面的注釋中指出nil變量代表的意義是很有必要的,就像我們上面例子里做的這樣。

下面是net/url包里Values類型定義的一部分。

package url
// Values maps a string key to a list of values.
type Values map[string][]string
// Get returns the first value associated with the given key,
// or "" if there are none.
func (v Values) Get(key string) string {
    if vs := v[key]; len(vs) > 0 {
        return vs[0]
    }
    return ""
}
// Add adds the value to key.
// It appends to any existing values associated with key.
func (v Values) Add(key, value string) {
    v[key] = append(v[key], value)
}

這個定義向外部暴露了一個map的命名類型,并且提供了一些能夠簡單操作這個map的方法。這個map的value字段是一個string的slice,所以這個Values是一個多維map??蛻舳耸褂眠@個變量的時候可以使用map固有的一些操作(make,切片,m[key]等等),也可以使用這里提供的操作方法,或者兩者并用,都是可以的:

m := url.Values{"lang": {"en"}} // direct construction
m.Add("item", "1")
m.Add("item", "2")
fmt.Println(m.Get("lang")) // "en"
fmt.Println(m.Get("q"))    // ""
fmt.Println(m.Get("item")) // "1"      (first value)
fmt.Println(m["item"])     // "[1 2]"  (direct map access)
m = nil
fmt.Println(m.Get("item")) // ""
m.Add("item", "3")         // panic: assignment to entry in nil map

對Get的最后一次調(diào)用中,nil接收器的行為即是一個空map的行為。我們可以等價地將這個操作寫成Value(nil).Get(“item”),但是如果你直接寫nil.Get(“item”)的話是無法通過編譯的,因為nil的字面量編譯器無法判斷其準(zhǔn)確類型。所以相比之下,最后的那行m.Add的調(diào)用就會產(chǎn)生一個panic,因為他嘗試更新一個空map。

由于url.Values是一個map類型,并且間接引用了其key/value對,因此url.Values.Add對這個map里的元素做任何的更新、刪除操作對調(diào)用方都是可見的。實際上,就像在普通函數(shù)中一樣,雖然可以通過引用來操作內(nèi)部值,但在方法想要修改引用本身時是不會影響原始值的,比如把他置換為nil,或者讓這個引用指向了其它的對象,調(diào)用方都不會受影響。(譯注:因為傳入的是存儲了內(nèi)存地址的變量,你改變這個變量本身是影響不了原始的變量的,想想C語言,是差不多的)

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容