2012-06-11 5 views
7

Я учусь, и я немного смущен, когда использовать указатели. В частности, при возврате функции struct из функции, когда целесообразно возвращать сам экземпляр структуры и когда целесообразно возвращать указатель на структуру?Когда это хорошая идея, чтобы вернуть указатель на структуру?

Пример кода:

type Car struct { 
    make string 
    model string 
} 

func Whatever() { 
    var car Car 

    car := Car{"honda", "civic"} 

    // ... 

    return car 
} 

Какие ситуации, в которых я хотел бы вернуть указатель, и где я бы не хотите? Есть хорошее правило?

+4

Это не C ... –

+0

Те же правила не применяются? – Carson

+2

нет, разные правила для разных языков. У каждого языка есть свои оговорки, и я лично не знаю «Go», поэтому я не могу говорить за него, но я знаю, что в C, вернув указатель на объект, выделенный в стеке, это гигантский no-no. –

ответ

12

Есть две вещи, которые вы хотите иметь в виду, производительности и API.

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

  1. hash/crc32: crc32.NewIEEE() функция возвращает тип указателя (на самом деле, интерфейс, но базовый тип является указателем). Экземпляр хэш-функции имеет состояние. Когда вы записываете информацию в хэш, она суммирует данные, поэтому, когда вы вызываете метод Sum(), он даст вам состояние одного экземпляра.

  2. time: time.Date функция возвращает a Time struct. Зачем? Время - это время. У него нет состояния. Это похоже на целое число, в котором вы можете сравнить их, префиксную математику на них и т. Д. Дизайнер API решил, что изменение времени не изменит текущий, а сделает новый. Как пользователь библиотеки, если я хочу время через месяц, мне нужен новый объект времени, чтобы не изменить текущий, который у меня есть. Время также составляет всего 3 слова. Другими словами, он мал и не будет увеличения производительности при использовании указателя.

  3. 10: big.NewInt() является интересным. Мы можем в значительной степени согласиться с тем, что при изменении big.Int вам часто понадобится новый. A big.Int не имеет внутреннего состояния, так почему это указатель? Ответ - просто производительность. Программисты поняли, что большие ints ... большие. Постоянное распределение каждый раз, когда вы выполняете математическую операцию, может оказаться непрактичным. Поэтому они решили использовать указатели и позволить программисту решить, когда выделять новое пространство.

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

+1

, мне пришлось прочитать это несколько раз, чтобы понять, что это действительно хороший ответ. Спасибо. – Carson

+0

Отличный ответ. Я новичок в Go, и здесь был приведен Стивен из # go-nuts в IRC. –

1

Очень losely, исключения могут проявляться в конкретных обстоятельствах:

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

Часто, когда вы хотите подражать объектно-ориентированному стилю, где у вас есть «объект», в котором хранятся состояние и «методы», которые могут изменять объект, тогда у вас будет функция «конструктор», которая возвращает указатель на структуру (подумайте об этом как о «ссылке на объект», как в других языках OO). Методы Mutator должны были бы быть методами типа pointer-to-struct вместо самого типа структуры, чтобы изменить поля «объекта», поэтому удобно иметь указатель на структуру вместо структуры само значение, так что все «методы» будут установлены в его методе.

Например, чтобы имитировать что-то вроде этого в Java:

class Car { 
    String make; 
    String model; 
    public Car(String myMake) { make = myMake; } 
    public setMake(String newMake) { make = myMake; } 
} 

Вы часто видите что-то вроде этого в Go:

type Car struct { 
    make string 
    model string 
} 
func NewCar(myMake string) *Car { 
    return &Car{myMake, ""} 
} 
func (self *Car) setMake(newMake string) { 
    self.make = newMake 
} 
+0

Итак, с go, не следует ли использовать ключевое слово 'new'? Я должен просто вернуть ссылку, как вы это иллюстрировали? Я смущен вашим примером. – Carson

+0

@Carson: ну, вы можете использовать 'new', но' new' инициализирует значение нулевому значению типа, которое для структур представляет собой все поля, инициализированные нулевым значением. Это может быть недействительно инициализированное значение для всех типов. Если нулевое значение подходит для инициализации вашего типа, вы можете просто использовать 'new'; но если вашему типу нужен пользовательский «конструктор», тогда вы должны определить свою собственную функцию, как я показал – newacct

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