Доступ к другой структуре по значению или по указателю - PullRequest
6 голосов
/ 02 мая 2010

Какая разница, когда вы получаете доступ к другой структуре по значению или по указателю?
Когда следует использовать каждый из них?

type foo_ struct {
    st uint8
    nd uint8
}

type bar struct {
    rd  uint8
    foo foo_
}

type barP struct {
    rd  uint8
    foo *foo_
}

Ответы [ 3 ]

5 голосов
/ 03 мая 2010

Если вы объявляете или выделяете переменную type bar, вы резервируете и инициализируете нулевую память для rd uint8 и foo foo_.Всегда есть одна переменная type foo_, встроенная в переменную type bar.

var b bar  // declare b

Если вы объявляете или выделяете переменную type barP, вы резервируете и инициализируете нулевую память для обоих rd uint8 и foo *foo_.Указатель с нулевым значением - это указатель nil.Переменная type foo_ не выделяется;Вы должны сделать это отдельно.Существует либо ноль (foo == nil), либо одна переменная type foo_, на которую указывает переменная type barP.Переменная type barP может указывать на ту же переменную type foo_, что и другие переменные type barP, совместно используя ту же копию переменной type foo_.Изменения в общей копии видны всеми переменными, которые на нее указывают.

var bp barP         // declare bp
bp.foo = new(foo_)  // allocate bp.foo

Какой из них использовать, зависит от свойств type bar против type barP.Какой тип более точно отражает проблему, которую вы пытаетесь решить?

Например, рассмотрим эту проблему со счетом.У нас всегда есть платежный адрес;мы всегда будем просить наши деньги.Однако мы часто отправляем по адресу выставления счета, но не всегда.Если адрес доставки nil, используйте платежный адрес.В противном случае используйте отдельный адрес доставки.У нас есть два склада, и мы всегда осуществляем доставку с одного или другого.Мы можем разделить два склада.Поскольку мы не отправляем счет-фактуру, пока заказ не будет отправлен со склада, местоположение склада никогда не будет nil.

type address struct {
    street string
    city   string
}

type warehouse struct {
    address string
}

type invoice struct {
    name      string
    billing   address
    shipping  *address
    warehouse *warehouse
}
2 голосов
/ 02 августа 2012

FAQ Golang теперь суммирует разницу между:

func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct)  valueMethod()   { } // method on value

Во-первых, и самое главное, нужно ли методу изменить приемник ?
Если это так, получатель должен быть указателем. (Срезы и карты являются ссылочными типами, поэтому их история немного сложнее, но, например, чтобы изменить длину среза в методе, получатель должен оставаться указателем.)
В приведенных выше примерах, если pointerMethod изменяет поля s, вызывающий будет видеть эти изменения, но valueMethod вызывается с копией аргумента вызывающего (это определение передачи значения), поэтому изменяет его make будет невидим для звонящего.
Кстати, приемники указателей идентичны ситуации в Java, хотя в Java указатели скрыты под крышками; необычные приемники стоимости Go.

Вторым является рассмотрение эффективности . Если получатель большой, например большая структура, будет гораздо дешевле использовать указатель получателя.

(Эта точка эффективности также показана в « Память, переменные в памяти и указатели »)

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

2 голосов
/ 02 мая 2010

Ответ в значительной степени не зависит от языка - эквивалент в C имеет те же проблемы.

Когда у вас есть встроенное значение (как в bar), тогда ваша структура достаточно велика, чтобы вместить полный текстподструктура и другая часть.

Если у вас есть указатель на значение (как в barP), то несколько структур типа barP могут совместно использовать один и тот же foo.Когда какая-либо из barP изменяет часть foo, на которую она указывает, она затрагивает все другие структуры barP, которые указывают на то же место.Кроме того, как следует из комментария, вам нужно управлять двумя отдельными объектами - barP и foo по сравнению с одним с простым типом bar.

В некоторых языках вам придется беспокоитьсяо висячих указателях и неинициализированных значениях и т. д .;Go - сборщик мусора и, как правило, более безопасен для типов, чем другие языки.

Итак, используйте указатель, когда вы хотите, чтобы несколько объектов barP совместно использовали один и тот же объект foo;в противном случае используйте явный объект-член, а не указатель на объект.

...