Skip to content

Instantly share code, notes, and snippets.

@Bingnan
Created April 11, 2017 07:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Bingnan/b0c102c676e46b647a05caa4e15592ae to your computer and use it in GitHub Desktop.
Save Bingnan/b0c102c676e46b647a05caa4e15592ae to your computer and use it in GitHub Desktop.
go语言中指针或值作为接收者的区别
10.6.3 指针或值作为接收者
鉴于性能的原因,recv 最常见的是一个指向 receiver_type 的指针(因为我们不想要一个实例的拷贝,如果按值调用的话就会是这样),特别是在 receiver 类型是结构体时,就更是如此了。
如果想要方法改变接收者的数据,就在接收者的指针类型上定义该方法。否则,就在普通的值类型上定义方法。
下面的例子 pointer_value.go 作了说明:change()接受一个指向 B 的指针,并改变它内部的成员;write() 接受通过拷贝接受 B 的值并只输出B的内容。注意 Go 为我们做了探测工作,我们自己并没有指出是是否在指针上调用方法,Go 替我们做了这些事情。b1 是值而 b2 是指针,方法都支持运行了。
示例 10.13 pointer_value.go:
package main
import (
"fmt"
)
type B struct {
thing int
}
func (b *B) change() { b.thing = 1 }
func (b B) write() string { return fmt.Sprint(b) }
func main() {
var b1 B // b1是值
b1.change()
fmt.Println(b1.write())
b2 := new(B) // b2是指针
b2.change()
fmt.Println(b2.write())
}
/* 输出:
{1}
{1}
*/
试着在 write() 中改变接收者b的值:将会看到它可以正常编译,但是开始的 b 没有被改变。
我们知道方法不需要指针作为接收者,如下面的例子,我们只是需要 Point3 的值来做计算:
type Point3 struct { x, y, z float }
// A method on Point3
func (p Point3) Abs float {
return math.Sqrt(p.x*p.x + p.y*p.y + p.z*p.z)
}
这样做稍微有点昂贵,因为 Point3 是作为值传递给方法的,因此传递的是它的拷贝,这在 Go 中合法的。也可以在指向这个类型的指针上调用此方法(会自动解引用)。
假设 p3 定义为一个指针:p3 := &Point{ 3, 4, 5}。
可以使用 p3.Abs() 来替代 (*p3).Abs()。
像例子 10.11(method1.go)中接收者类型是 *TwoInts 的方法 AddThem(),它能在类型 TwoInts 的值上被调用,这是自动间接发生的。
因此 two2.AddThem 可以替代 (&two2).AddThem()。
在值和指针上调用方法:
可以有连接到类型的方法,也可以有连接到类型指针的方法。
但是这没关系:对于类型 T,如果在 *T 上存在方法 Meth(),并且 t 是这个类型的变量,那么 t.Meth() 会被自动转换为 (&t).Meth()。
指针方法和值方法都可以在指针或非指针上被调用,如下面程序所示,类型 List 在值上有一个方法 Len(),在指针上有一个方法 Append(),但是可以看到两个方法都可以在两种类型的变量上被调用。
示例 10.14 methodset1.go:
package main
import (
"fmt"
)
type List []int
func (l List) Len() int { return len(l) }
func (l *List) Append(val int) { *l = append(*l, val) }
func main() {
// 值
var lst List
lst.Append(1)
fmt.Printf("%v (len: %d)", lst, lst.Len()) // [1] (len: 1)
// 指针
plst := new(List)
plst.Append(2)
fmt.Printf("%v (len: %d)", plst, plst.Len()) // &[2] (len: 1)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment