Вопрос о приемнике, использующем struct in go - PullRequest
0 голосов
/ 20 января 2019

использование получателя

package main

import "fmt"

type Person struct {
    name string
    age  int
}

func (p *Person) greeting1() {

    fmt.Println(p)  //&{0}
    fmt.Println(&p) //0xc000086018
    fmt.Println("Hello~")
}

func (p Person) greeting2() {
    fmt.Println(p)  //{0}
    fmt.Println(&p) //&{0}
    fmt.Println("Hello~")
}

type Student struct {
    //p Persion -> has a 
    Person // -> is a 
    school string
    grade  int
}

func test1(p Student) {
    fmt.Println(p)
}

func test2(p *Student){
    fmt.Println(p)
}

func main() {
    var s Student
    //s.p.greeting()
    s.greeting1()
    s.greeting2()

    test1(s)
    //test2(s) -> error 
}

Когда я использую функцию в golang, если я объявляю переменную значения, когда я использую созданную мной функцию, мне приходилось помещать только переменную значения в параметре.например,

a int= 10;
func func1(param int){
 fmt.Println(fmt)
}

, несмотря на то, что я объявил Person как структуру значений в структуре Student, как вы видите, мой код, функции приемника (приветствие1, приветствие2) получают два параметра типа: Person и Person.Я не могу понять, почему функция приветствия1 работает без ошибок, несмотря на то, что я поместил значение переменной в качестве параметра.Спасибо за помощь, ребята.

Ответы [ 2 ]

0 голосов
/ 22 января 2019

На самом деле Go - это объектно-ориентированный язык по-своему. Он использует структуру вместо классов. Если вы хотите иметь класс, вы должны создать структуру и связать с ней некоторые функции с помощью указателя этой структуры, поэтому позже вы можете создать много независимых объектов из вашей структуры. Вы можете использовать функцию new, которая будет возвращать указатель вашей структуры. Как:

p:=new(person)
p.name = "joe"
p.age = 41
p.greeting1()
0 голосов
/ 20 января 2019

Упрощение вашего примера кода, чтобы сосредоточиться на вопросе / проблеме под рукой:

package main

import "fmt"

type Person struct {
    name string
    age  int
}

func (p *Person) greeting1() {
    fmt.Println(p)
    fmt.Println(&p)
}

func main() {
    var p Person
    p.name = "joe"
    p.age = 41
    p.greeting1()
}

Печать:

&{joe 41}
0xc00000c028

Короче говоря, вызов p.greeting1() работает, потому что Go видит, что Person (тип p) имеет метод greeting1, определенный на приемнике указателя. Таким образом, он обрабатывает вызов p.greeting1() как эквивалент (&p).greeting1(). Как сказал mkopriva в комментарии, это ясно изложено в спецификации Go :

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

Это сделано для удобства. Поскольку приемники указателей весьма необходимы и популярны в Go - они нужны вам всякий раз, когда ваш метод изменяет базовый объект - это помогает писать более чистый код. Вы не должны быть вынуждены писать (&p).greeting1(), где будет делать p.greeting1() - эта замена однозначна, поскольку компилятор знает, какие методы определены в Person и с какими приемниками.

В Effective Go есть раздел указатель против получателей значения , который вы должны прочитать. Вот одна уместная цитата:

Когда значение адресуемое, язык заботится об общем случай вызова метода указателя на значение путем вставки адреса Оператор автоматически. В нашем примере переменная b является адресуемой, так что мы можем вызвать его метод Write с помощью только b.Write. Компилятор переписать это на (& b). Напишите нам.

...