Passaggio per valore vs per riferimento
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 21 di 107.
Go è un linguaggio passaggio per valore. Quando passi una variabile a una funzione, Go crea una copia di quel valore. Questo significa che le modifiche all’interno della funzione non influiscono sulla variabile originale.
func double(n int) {
n = n * 2
}
func main() {
x := 10
double(x)
fmt.Println(x) // 10 (invariato)
}Lo stesso vale per le struct. Passare una struct a una funzione copia tutti i suoi campi, quindi le modifiche apportate all'interno della funzione vanno perse.
type Person struct {
Name string
Age int
}
func birthday(p Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(alice)
fmt.Println(alice.Age) // 30 (invariato)
}Per modificare il valore originale, passa invece un puntatore. Il puntatore stesso viene copiato, ma continua a puntare allo stesso indirizzo di memoria, consentendoti di modificare i dati originali.
func birthday(p *Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(&alice)
fmt.Println(alice.Age) // 31 (modified!)
}Usa i puntatori quando devi modificare il valore originale o quando passi struct di grandi dimensioni per evitare costose copie. Usa i valori quando vuoi proteggere i dati originali da modifiche involontarie.
Sfida
FacileCreiamo un sistema di carrello della spesa che dimostri la differenza tra passare le struct per valore o tramite puntatore. Vedrai direttamente che solo i parametri puntatore possono modificare i dati originali.
Organizzerai il codice in due file:
cart.go: Definisci una structCartItemcon i campiName(string),Price(float64) eQuantity(int). Crea due funzioni che tentano di aggiornare la quantità:UpdateQuantityByValuericeve unCartItem(per valore) e una nuova quantità, poi imposta la quantità dell'articolo sul nuovo valoreUpdateQuantityByPointerriceve un*CartItem(puntatore) e una nuova quantità, poi imposta la quantità dell'articolo sul nuovo valore
TotalsuCartItemche restituisce il prezzo moltiplicato per la quantità.main.go: Leggi i dettagli dell'articolo dall'input, crea un CartItem, poi mostra entrambi i metodi di aggiornamento. Prima prova ad aggiornare con la funzione per valore e mostra il risultato, poi aggiorna con la funzione per puntatore e mostra nuovamente il risultato.
Verranno forniti i seguenti input:
- Riga 1: Nome dell'articolo
- Riga 2: Prezzo (float)
- Riga 3: Quantità iniziale (intero)
- Riga 4: Nuova quantità da impostare (intero)
Formato dell'output:
Initial: [Name] x[Quantity] = $[Total]
After value update: [Name] x[Quantity] = $[Total]
After pointer update: [Name] x[Quantity] = $[Total]Ad esempio, dati Laptop, 999.99, 1 e 3, l'output dovrebbe essere:
Initial: Laptop x1 = $999.99
After value update: Laptop x1 = $999.99
After pointer update: Laptop x3 = $2999.97Nota che la quantità rimane 1 dopo l'aggiornamento per valore (la funzione ha modificato una copia), ma cambia in 3 dopo l'aggiornamento tramite puntatore (la funzione ha modificato l'originale). Formatta i totali con due cifre decimali.
Provalo tu
package main
import (
"fmt"
)
func main() {
// Leggi l'input
var name string
var price float64
var initialQty, newQty int
fmt.Scanln(&name)
fmt.Scanln(&price)
fmt.Scanln(&initialQty)
fmt.Scanln(&newQty)
// TODO: Crea un CartItem con i valori di input
// TODO: Stampa lo stato iniziale usando il formato:
// fmt.Printf("Initial: %s x%d = $%.2f\n", ...)
// TODO: Prova ad aggiornare la quantità usando UpdateQuantityByValue
// Poi stampa il risultato (nota che la quantità non cambierà)
// TODO: Aggiorna la quantità usando UpdateQuantityByPointer
// Poi stampa il risultato (nota che la quantità cambierà)
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP in Go
File esterniWorkspace e moduli GoPacchetti e importazioniNomi esportati e non esportatiIntroduzione all’OOP in GoGli struct come classiDefinire metodi sugli structReceiver con puntatore vs receiver con valoreInizializzazione degli structFunzioni costruttriciRiepilogo - Calcolatrice semplice4Interfacce
Introduzione alle interfacceImplementazione implicitaL’interfaccia come contrattoInterfaccia vuota (any)Asserzione di tipoSwitch sul tipoComposizione di interfacceInterfacce Stringer ed ErrorRipasso - Calcolatore di forme7Incapsulamento
Campi esportati e non esportatiIncapsulamento a livello di packageMetodi Getter e SetterNascondimento delle informazioni in GoRiepilogo - Registri degli studenti10Tipi generici (Go 1.18+)
Introduzione ai tipi genericiParametri di tipoVincoli di tipoStruct genericheSoluzione alternativa per i metodi genericiRiepilogo - Collezione generica13Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello Abstract FactoryModello ObserverModello Strategy2Approfondimento su tipi e struct
Tipi di base e compostiDefinizioni di tipi personalizzatiTag delle structStruct anonimeStruct annidateValori zero e predefinitiRipasso - Rubrica5Composizione invece dell'ereditarietà
Perché Go non ha l'ereditarietàFondamenti dell'embedding delle structPromozione dei metodiEmbedding di più structEmbedding e aggregazione a confrontoMascheramento dei metodi embeddedRiepilogo: gerarchia dei dipendenti8Gestione degli errori e OOP
L'interfaccia errorTipi di errore personalizzatiWrapping degli errori (fmt.Errorf)Errori sentinellaerrors.Is() ed errors.As()Panic, Defer e RecoverRiepilogo - Parser di file11Libreria standard e OOP
io.Reader e io.Writersort.InterfaceInterfaccia fmt.Stringerencoding/json con le structInterfaccia http.HandlerRipasso - modelli REST API14Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeMiddleware come Decorator3Puntatori e memoria
Concetti base dei puntatori in GoPuntatori a structPassaggio per valore vs per riferimentoLa funzione new()Garbage collection in GoRiepilogo - Costruttore di liste concatenate6Polimorfismo in Go
Polimorfismo tramite interfacceDuck typing in GoRegole di soddisfacimento delle interfacceCollezioni polimorficheIniezione delle dipendenzeRiepilogo - Elaboratore di pagamenti9Concorrenza e OOP
Basi delle goroutineCanali e comunicazioneCanali bufferizzati vs non bufferizzatiIstruzione selectsync.Mutex e sync.RWMutexsync.WaitGroupProgettazione di struct thread-safeRiepilogo: worker poolEsercitati da solo: Compilatore Go online