2012-06-11 12 views
7

Sto imparando Go, e sono un po 'confuso su quando usare i puntatori. In particolare, quando si restituisce un struct da una funzione, quando è opportuno restituire l'istanza struct stessa e quando è opportuno restituire un puntatore alla struct?Quando è consigliabile restituire un puntatore a una struttura?

codice Esempio:

type Car struct { 
    make string 
    model string 
} 

func Whatever() { 
    var car Car 

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

    // ... 

    return car 
} 

Quali sono le situazioni in cui vorrei restituire un puntatore, e dove avrei non vuole? C'è una buona regola empirica?

+4

Questo non è C ... –

+0

Le stesse regole non si applicano? – Carson

+2

no, diverse regole per diverse lingue. Ogni lingua ha i suoi avvertimenti, e io personalmente non conosco Go, quindi non posso parlarne, ma so che in C, restituire un puntatore a un oggetto allocato nello stack è un no-no gigante. –

risposta

12

Ci sono due cose che si desidera tenere a mente, prestazioni e API.

Come si usa un'automobile? È un oggetto che ha uno stato? È una grande struttura? Sfortunatamente, è impossibile rispondere quando non ho idea di cosa sia una macchina. Sinceramente, il modo migliore è vedere cosa fanno gli altri e copiarli. Alla fine, hai una sensazione per questo genere di cose. Descriverò ora tre esempi dalla libreria standard e spiegherò perché penso che abbiano usato ciò che hanno fatto.

  1. hash/crc32: La funzione crc32.NewIEEE() restituisce un tipo di puntatore (in realtà, un'interfaccia, ma il tipo sottostante è un puntatore). Un'istanza di una funzione hash ha stato. Quando si scrivono le informazioni in un hash, si riassumono i dati, quindi quando si chiama il metodo Sum(), verrà fornito lo stato di tale istanza.

  2. time: la funzione time.Date restituisce una struttura Time. Perché? Un tempo è un tempo Non ha stato. È come un intero in cui è possibile confrontarli, preformare la matematica su di essi, ecc. Il progettista dell'API ha deciso che una modifica a un tempo non avrebbe cambiato quella attuale, ma ne avrebbe creata una nuova. Come utente della biblioteca, se voglio il tempo tra un mese a partire da ora, vorrei un nuovo oggetto temporale, non per cambiare quello attuale che ho. Un tempo è anche solo 3 parole di lunghezza. In altre parole, è piccolo e non ci sarebbe alcun guadagno di prestazioni nell'uso di un puntatore.

  3. math/big: big.NewInt() è un'interessante. Siamo in grado di concordare sul fatto che quando modifichi uno big.Int, spesso ne vuoi uno nuovo. A big.Int non ha stato interno, quindi perché è un puntatore? La risposta è semplicemente prestazioni. I programmatori hanno capito che i big ints sono ... grandi. Assegnare costantemente ogni volta che esegui un'operazione matematica potrebbe non essere pratico. Quindi, hanno deciso di utilizzare i puntatori e consentire al programmatore di decidere quando allocare nuovo spazio.

Ho risposto alla tua domanda? Probabilmente no. È una decisione di progettazione e devi capirlo caso per caso. Io uso la libreria standard come guida quando sto progettando le mie librerie personali. Tutto dipende dal giudizio e da come ti aspetti che il codice client usi i tuoi tipi.

+1

ho dovuto leggerlo alcune volte per capire che questa è davvero una buona risposta. grazie. – Carson

+0

Ottima risposta. Sono nuovo per andare, ed è stato riferito da Stephen da # go-nuts su IRC. –

1

Molto losely, le eccezioni sono suscettibili di presentarsi in circostanze specifiche:

  • restituire un valore quando è veramente piccolo (non più di poche parole).
  • Restituisce un puntatore quando il sovraccarico di copia danneggerebbe in modo significativo le prestazioni (le dimensioni sono un sacco di parole).
2

Spesso, quando si vuole imitare uno stile orientato agli oggetti, dove si ha un "oggetto" che memorizza stato e "metodi" che possono alterare l'oggetto, allora si avrebbe una funzione di "costruttore" che ritorna un puntatore a una struttura (pensatela come "riferimento all'oggetto" come in altre lingue OO). I metodi del Mutator dovrebbero essere metodi del tipo pointer-to-the-struct invece del tipo struct stesso, per cambiare i campi dell '"oggetto", quindi è conveniente avere un puntatore alla struct invece di una struct valore stesso, in modo che tutti i "metodi" siano nel suo set di metodi.

Ad esempio, per simulare qualcosa di simile in Java:

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

si sarebbe spesso di vedere qualcosa di simile in 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

quindi, con go, non dovrei usare la parola chiave 'new'? dovrei solo restituire un riferimento come hai illustrato? sono confuso dal tuo esempio – Carson

+0

@Carson: beh, puoi usare 'new', ma' new' inizializza il valore sul valore zero del tipo, che per structs è tutti i campi inizializzati sul valore zero. Questo potrebbe non essere un valore validato per tutti i tipi. Se il valore zero va bene per inizializzare il tuo tipo, allora puoi semplicemente usare 'new'; ma se il tuo tipo ha bisogno di un "costruttore" personalizzato, allora dovresti definire la tua funzione come ho mostrato – newacct

Problemi correlati