go 语言中的指针接收器和值接收器
在 Go 中,定义结构体有两种接收器可以选择
type T struct {
a int
}
func (t T) Fv(a int) int {
fmt.Println("in fv")
t.a = a
return a
}
func (t *T) Fp(a int) int {
fmt.Println("in fp")
t.a = a
return a
}
区别和联系
T 和 *T 是两个不同的类型。每个类型都有自己的方法集。
T 的方法集包含了所有以 T 为 receiver 的方法。
*T 的方法集包含了所有以 *T 或 T 为 receiver 的方法。
作为变量直接调用两者都可以,如果修改成员变量效果不一样
当以 T 或 *T 类型的变量作为 receiver 去调用一个方法的时候,不管该方法定义的 receiver 类型是什么,都可以调用。普通方法调用只看类型签名(类名),不看方法集(method_set),但是效果不一样。
func main() {
tv := T{}
tv.Fp(1)
tv.Fv(1)
tp := &T{}
tp.Fp(2)
tp.Fv(2)
}
// in fp
// in fv
// in fp
// in fv
这里两个类型都可以调用其方法。
func main() {
tv := T{}
tv.a = 0
tv.Fp(1)
fmt.Println("tv.fp: ", tv.a)
tv.a = 0
tv.Fv(1)
fmt.Println("tv.fv: ", tv.a)
tp := &T{}
tp.a = 0
tp.Fp(1)
fmt.Println("tp.fp: ", tp.a)
tp.a = 0
tp.Fv(1)
fmt.Println("tp.fv: ", tp.a)
}
// output
in fp
tv.fp: 1
in fv
tv.fv: 0
in fp
tp.fp: 1
in fv
tp.fv: 0
可以看到只有 fp 的方法真正的修改了原始的值。可以这样理解,接收者可以看作是函数的第一个参数,即这样的: func Fv(t T, a int)
, func Fp(t *T, a int)
。所以只有传指针进去的才能修改原始变量的值,传值的都是传的原始变量的副本,修改的也是副本的值,原始变量不会变。
当 T 满足了一个 interface 的时候,*T 也一定会满足这个 interface。反之则不然。
用下面的代码验证
type IFV interface {
Fv(int) int
}
type IFP interface {
Fp(int) int
}
func main() {
var ifv IFV
var ifp IFp
ifv = T{}
ifp = T{}
ifv = &T{}
ifp = &T{}
}
// output
//./main.go:30:6: cannot use T literal (type T) as type IFV in assignment:
// T does not implement IFV (Fv method has pointer receiver)
什么时候使用指针接收器
-
如果要在使用中更改接收器的状态,修改它的值,就需要使用指针接收器。(对值接收器的任何修改都是对该副本的本地修改)
-
如果定义方法的结构体非常大,复制它将比使用指针接收器代价更大。
什么时候使用值接收器
-
如果不需要编辑接收器的值。
-
值接收器是并发安全的,指针接收器不是的。
参考
https://github.com/golang/go/wiki/CodeReviewComments#receiver-type
什么时候使用指针接收器
Read other posts