2015-07-01 3 views
1

Некоторые типы в Go являются ссылочными типами: карты, срезы, каналы, функции и методы.Перейти: ссылочные типы в качестве аргументов

Иногда вам нужно использовать указатели для ссылок. Например,

type Stack []interface{} 
func (stack *Stack) Push(x interface{}) { 
    *stack = append(*stack, x) 
} 

Вам это нужно, потому что все аргументы передаются путем копирования значения, и append() может понадобиться перераспределить память емкостью Срез не является достаточно большим. Я понимаю.

Первый вопрос. Как насчет map? Если у меня есть пользовательский тип, основанный на map, должен ли я лучше всегда передать указатель на него, если ожидается какой-либо ключ: значения вставки или удаления значений?

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

Извините, если это базовое значение, но я действительно хочу получить хорошее представление о предмете.

+0

Вы вводите в заблуждение дескриптор среза с базовым массивом. В Go нет ссылочных типов. У вас есть ценности, у вас есть указатели. Указатель - это «ссылка». И я не знаю, что делает ваш образец кода, но это, безусловно, не обязательно. Стоит прочитать http://blog.golang.org/go-slices-usage-and-internals – evanmcdonnal

+0

@evanmcdonnal: no, для которого требуется указатель, потому что append может выделять новый массив, изменяя внутренний указатель данных, а также и значения len и cap. http://play.golang.org/p/Cm8AQXpK5J – JimB

+0

@ JimB да, я заметил, что, возившись с ним на детской площадке. Однако эта необходимость вызвана глупой конструкцией. Просто определите метод с получателем и возвращаете тип 'Stack' и выполните' myStack = myStack.Push (x) '. Или еще лучше, не определяйте ничего из этого, потому что это всего лишь пустая трата вашей системной ОЗУ. Как единственная причина, по которой это происходит, связано с тем, что append обрабатывается дескриптором среза как значение, возвращая новое после завершения действия. Затем он делает что-то непоследовательное в создании своего типа, который представляет собой не что иное, как цепочку фрагментов, метод с использованием указателя. – evanmcdonnal

ответ

5

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

  • ломтиков: Используйте указатель, когда вы, возможно, потребуется изменить длину или емкость, которая изменяет значение среза.
  • maps: Не используйте указатель, так как значение карты не изменяется с изменениями.
  • функции и методы: Не используйте указатель, тот же эффект имеет значения функции.
  • chan: Не используйте указатель.

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

+0

Это то, что мне нужно, спасибо. – oopcode

+0

Вам необязательно синхронизировать изменения с указателями на chans. Если модификации происходят только с помощью одного горутина, синхронизация не нужна (как и со всеми указателями). – rightfold

+0

@ rightfold: ну, ничего не нужно синхронизировать, если есть доступ только в одном goroutine, но даже если значение только * изменено * в одном goroutine, несинхронизированные параллельные чтения этого значения по-прежнему являются условием гонки. Хотя это условие совпадает с общим значением chan в той же области. Я удалю формулировку, поскольку это, вероятно, более запутанно, чем полезно. – JimB

1

На самом деле не существует дихотомии между «типами значений» и «ссылочными типами». «Тип ссылки» используется только для описания типа значения, чье «значение» состоит целиком из одного указателя.

Это верно для типов карт и каналов, которые в основном представляют собой типы указателей для внутренней структуры. Но это не совсем верно для срезов, потому что срез представляет собой составной тип (в основном struct), состоящий из двух целых значений (длина &) и указателя (для элементов). Таким образом, это «ссылочный тип» по отношению к элементам, к которым осуществляется доступ через указатель, но это «тип значения» по отношению к длине и емкости.

Добавление к срезу зависит от его длины и потенциальной емкости, поэтому необходимо изменить «значение» среза, тогда как назначение элементов на месте просто использует указатель и, следовательно, не нужно изменять значение "от среза. Вам также может потребоваться изменить «значение» среза, если вы хотите, чтобы он изменил указатель на то же, что и на другой фрагмент (который вы делаете, назначая срезу).

Это похоже на «ссылочные типы», карты и каналы. Изменение «содержимого» карты или канала (находящегося в материале, на которое указывает указатель) не требует изменения «значения» карты или канала. Но если вы хотите изменить указатель, чтобы указать на другую базовую карту или канал, то вы измените «значение» карты или переменной канала.

Смежные вопросы