Go方法对值和方法对指针有什么区别

  • 本文关键字:方法 区别 Go 指针 go
  • 更新时间 :
  • 英文 :


我想知道指针上有方法和值上有方法的区别。这两种方法如何在标准结构实例和结构指针上工作。


将接收器定义为值

格式:

func (r T) Xxx() {}

可以通过值或指针进行调用。

当使用指针调用时,该值将自动传递,(它实际上使用*来获取调用方的值,并将其传递)。


将接收器定义为指针

格式:

func (r *T) Xxx() {}

原则上,应该只使用指针调用,但这不是必需的。

因为当用值而不是指针调用时,编译器会在可能的情况下处理它:

  • 如果值是可寻址的,(对于go中的大多数数据类型都是如此)
    然后编译器将获取地址(通过&),并自动传递
    这使得可以直接调用具有值的指针方法(我想这在go中很常见,它让程序员的生活更轻松)
  • 如果该值不可寻址,则(这种情况很少见,但存在)
    然后需要显式传递地址,否则编译时会出错
    例如map的元素是不可寻址的

提示

  • 如果合适的话,定义方法时首选指针调用方
    原因:

    • 它可以修改调用者
    • 对于复杂的调用者来说,它更为轻量级
  • 传递给方法的内容取决于方法签名,而不是如何调用它(这与param类似)

    • 当声明调用者为指针(r *T)时,它传递指针
    • 当将调用者声明为值(r T)时,它传递原始调用者的副本
  • T本身不能是指针

代码

这是我在学习这个功能时写的一个go代码。

它在main()中调用的2个函数分别测试了这2个特性。

method_learn.go:

// method - test
package main
import (
"fmt"
"math"
)
type Vertex struct {
x float64
y float64
}
// abs, with pointer caller,
func (v *Vertex) AbsPointer() float64 {
return math.Sqrt(v.x*v.x + v.y*v.y)
}
// scale, with pointer caller,
func (v *Vertex) ScalePointer(f float64) *Vertex {
v.x = v.x * f
v.y = v.y * f
return v
}
// abs, with value caller,
func (v Vertex) AbsValue() float64 {
return math.Sqrt(v.x*v.x + v.y*v.y)
}
// test - method with pointer caller,
func pointerCallerLearn() {
vt := Vertex{3, 4}
fmt.Printf("Abs of %v is %v. (Call %s method, with %s)n", vt, vt.AbsPointer(), "pointer", "value")        // call pointer method, with value,
fmt.Printf("Abs of %v is %v. (Call %s method, with %s)nn", vt, (&vt).AbsPointer(), "pointer", "pointer") // call pointer method, with pointer,
// scala, change original caller,
fmt.Printf("%v scale by 10 is: %v (Call %s method, with %s)n", vt, vt.ScalePointer(10), "pointer", "value")      // call pointer method, with value,
fmt.Printf("%v scale by 10 is: %v (Call %s method, with %s)n", vt, (&vt).ScalePointer(10), "pointer", "pointer") // call pointer method, with pointer,
}
// test - method with value caller,
func valueCallerLearn() {
vt := Vertex{3, 4}
fmt.Printf("Abs of %v is %v. (Call %s method, with %s)n", vt, (&vt).AbsValue(), "value", "pointer") // call value method, with pointer,
fmt.Printf("Abs of %v is %v. (Call %s method, with %s)n", vt, vt.AbsValue(), "value", "value")      // call value method, with value,
}
func main() {
// pointerCallerLearn()
valueCallerLearn()
}

只需修改main(),并通过go run method_test.go运行,然后检查输出以查看其工作方式。

它们之间的最大区别是值接收器被复制*。所以,若你们想改变你们的接收器,你们必须使用指针。观察:

package main
import (
"fmt"
)
type Person struct {
Age int
}
func (p Person) GrowUp1() {
p.Age++
}
func (p *Person) GrowUp2() {
p.Age++
}
func main() {
p := Person{Age: 20}
fmt.Println(p)
p.GrowUp1()
fmt.Println(p)

p.GrowUp2()
fmt.Println(p)
}
// {20}
// {20}
// {21}

*指针也会自然复制。但由于它们是指针,指针的副本仍然指向同一个对象

最新更新