在值上使用方法和在指针上使用方法有什么区别?

时间:2019-02-01 09:35:46

标签: go

我想知道在指针上使用方法和在值上使用方法有什么区别。两种方法如何在标准结构实例以及结构指针上工作。

2 个答案:

答案 0 :(得分:2)


将接收者定义为值

格式:

func (r T) Xxx() {}

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

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


将接收方定义为指针

格式:

func (r *T) Xxx() {}

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

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

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

提示

  • 在定义方法时(如果合适),首选指针调用程序。
    原因:

    • 它可以修改呼叫者。
    • 对于复杂的呼叫者而言,它更为轻巧。
  • 传递给方法的内容取决于方法签名,而不是您如何称呼它(这与param相似)

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

代码

而且,这是我在学习此功能时编写的代码。

它在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)\n\n", 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运行,然后检查输出以查看其工作原理。

答案 1 :(得分:0)

它们之间的最大区别是复制了值接收者 * 。因此,如果要更改接收器,则必须使用指针。观察:

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}

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