Перейти метод на значение и метод на указатели Какая разница? - PullRequest
0 голосов
/ 01 февраля 2019

Я хочу знать, в чем отличие наличия методов для указателя и метода для значений.Как оба метода работают на стандартном экземпляре структуры, а также на указателе структуры.

Ответы [ 2 ]

0 голосов
/ 01 февраля 2019

Большая разница между ними заключается в том, что копируются получатели значений *.Так что, если вы хотите изменить ваш приемник, вы должны использовать указатель.Обратите внимание:

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}

* Указатели тоже копируются, естественно.Но поскольку они являются указателями, копия указателя по-прежнему указывает на тот же объект.

0 голосов
/ 01 февраля 2019

Определение получателя как значения

Формат:

func (r T) Xxx() {}

Может вызывать либо по значению, либо по указателю.

При вызове с указателем значениебудет передан автоматически, (на самом деле он использует * для получения значения вызывающего и передачи его).


Определение получателя в качестве указателя

Формат:

func (r *T) Xxx() {}

В принципе, должен вызываться только с указателем, но это не обязательно.

Поскольку при вызове со значением вместо указателя компилятор позаботится об этом, когда это возможно:

  • Если значение адресуемое, (что верно для большинства типов данных в go) .
    Тогда компилятор возьмет адрес (через &) и передаст его автоматически.
    Это позволяет напрямую вызывать метод указателя со значением, (это довольно распространенное явление в go, и это облегчает жизнь программиста) .
  • Если значение не адресуемое, (что встречается редко, но существует) .
    Тогда нужно явно передать адрес,в противном случае вы получите сообщение об ошибке при компиляции.
    , например, элемент map не адресуем.

Tips

  • Предпочтительнее указатель вызывающей стороны, при определении метода, если это правильно.
    Причины:

    • Это может изменить вызывающего.
    • Это более легкий для сложного вызывающего.
  • То, что передается методу, зависит от сигнатуры метода, а не от того, как вы его называете (это аналогично параметру) .

    • Когда объявляется вызывающий как указатель (r *T), он передает указатель.
    • Когда объявляется вызывающий как значение (r T), он передает копию исходного звонящего.
  • T сам по себе не может быть указателем.

Code

И вот код go, который я написал, изучая эту функцию.

2 функции, вызываемые в main(), проверяют 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, затем проверьте вывод, чтобы увидеть, как он работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...