Подтвердить что ты не робот

Голанские указатели

В настоящее время я участвую в программировании на языке Go. У меня возникают некоторые трудности с пониманием указателей Go (и мой C/С++ уже далеко...). Например, в Tour of Go # 52 (http://tour.golang.org/#52) я читал:

type Vertex struct {
    X, Y float64
}

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := &Vertex{3, 4}
    fmt.Println(v.Abs())
}

Но если вместо

func (v *Vertex) Abs() float64 {
[...]
v := &Vertex{3, 4}

Я написал:

func (v Vertex) Abs() float64 {
[...]
v := Vertex{3, 4}

Или даже:

func (v Vertex) Abs() float64 {
[...]
v := &Vertex{3, 4}

и наоборот:

func (v *Vertex) Abs() float64 {
[...]
v := Vertex{3, 4}

Я получил тот же результат. Есть ли разница (память и т.д.)?

4b9b3361

Ответ 1

Существуют два разных правила языка Go, используемые вашими примерами:

  • Можно получить метод с приемником указателя из метода с приемником значений. Таким образом, func (v Vertex) Abs() float64 автоматически сгенерирует реализацию дополнительного метода:

    func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X+v.Y*v.Y) }
    func (v *Vertex) Abs() float64 { return Vertex.Abs(*v) }  // GENERATED METHOD
    

    Компилятор автоматически найдет сгенерированный метод:

    v := &Vertex{3, 4}
    v.Abs()  // calls the generated method
    
  • Go может автоматически принимать адрес переменной. В следующем примере:

    func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X+v.Y*v.Y) }
    func main() {
        v := Vertex{3, 4}
        v.Abs()
    }
    

    выражение v.Abs() эквивалентно следующему коду:

    vp := &v
    vp.Abs()
    

Ответ 2

Есть различия. Например, форма получателя не указателя заставляет метод работать с копией. Таким образом, метод не может мутировать экземпляр, на который он был вызван, - он может получить доступ только к копии. Что может быть неэффективным с точки зрения, например, время/производительность/потребление памяти и т.д.

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

Подробнее здесь.

Ответ 3

Разница заключается в прохождении через реферив против пропущенного значения.

В func f(v Vertex) аргумент копируется в параметр v. В func f(v *Vertex) передается указатель на существующий экземпляр Vertex.

При использовании методов для вас может быть выполнено разделение на разы, поэтому вы можете иметь метод func (v *Vertex) f() и вызывать его, не указав сначала: v := Vertex{...}; v.f(). Это всего лишь зерно синтаксического сахара, AFAIK.

Ответ 4

В этих примерах есть два основных отличия:

func (v *Vertex) Abs()....

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

v := Vertex{1,3}
v.Abs() // This will result in compile time error
&v.Abs() // But this will work

С другой стороны,

func (v Vertex) Abs() ....

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

v := Vertex{1,3}
v.Abs() // This will work, v will be copied.
&v.Abs() // This will also work, v will also be copied.

Вы можете объявить как func (v *Vertex), так и func (v Vertex).

Ответ 5

Как указано в описании

Вызов метода x.m() действителен, если набор методов (тип) x содержит m, а список аргументов может быть назначен списку параметров m. Если x адресуется, а набор методов & x содержит m, x.m() является сокращением для (& x).m():

В вашем случае v.Abs() является сокращением для & v.Abs(), если метод адресуется.