Übergabe per Wert vs. Referenz
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 21 von 107.
Go ist eine Pass-by-Value-Sprache. Wenn Sie eine Variable an eine Funktion übergeben, erstellt Go eine Kopie dieses Wertes. Das bedeutet, dass Änderungen innerhalb der Funktion die ursprüngliche Variable nicht beeinflussen.
func double(n int) {
n = n * 2
}
func main() {
x := 10
double(x)
fmt.Println(x) // 10 (unverändert)
}Das Gleiche gilt für Structs. Das Übergeben eines Structs an eine Funktion kopiert alle seine Felder, sodass Änderungen innerhalb der Funktion verloren gehen.
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 (unverändert)
}Um den ursprünglichen Wert zu ändern, übergeben Sie stattdessen einen Pointer. Der Pointer selbst wird kopiert, aber er zeigt immer noch auf dieselbe Speicheradresse, was es Ihnen ermöglicht, die ursprünglichen Daten zu ändern.
func birthday(p *Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(&alice)
fmt.Println(alice.Age) // 31 (geändert!)
}Verwenden Sie Pointer, wenn Sie den ursprünglichen Wert ändern müssen oder wenn Sie große Structs übergeben, um teures Kopieren zu vermeiden. Verwenden Sie Werte, wenn Sie die ursprünglichen Daten vor unbeabsichtigten Änderungen schützen möchten.
Aufgabe
EinfachErstelle ein Warenkorbsystem, das den Unterschied zwischen der Übergabe von Strukturen als Wert und als Zeiger veranschaulicht. Du wirst unmittelbar sehen, dass nur Zeigerparameter die ursprünglichen Daten ändern können.
Du organisierst deinen Code über zwei Dateien:
cart.go: Definiere eineCartItem-Struktur mit den FeldernName(string),Price(float64) undQuantity(int). Erstelle zwei Funktionen, die versuchen, die Menge zu aktualisieren:UpdateQuantityByValuenimmt einCartItem(als Wert) und eine neue Menge entgegen und setzt anschließend die Menge des Artikels auf den neuen WertUpdateQuantityByPointernimmt ein*CartItem(einen Zeiger) und eine neue Menge entgegen und setzt anschließend die Menge des Artikels auf den neuen Wert
Total-Methode fürCartItem, die den mit der Menge multiplizierten Preis zurückgibt.main.go: Lies die Artikeldaten aus der Eingabe ein, erstelle ein CartItem und führe anschließend beide Aktualisierungsansätze vor. Versuche zuerst, die Aktualisierung mit der Wertfunktion durchzuführen, und gib das Ergebnis aus. Aktualisiere danach mit der Zeigerfunktion und gib das Ergebnis erneut aus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Artikelname
- Zeile 2: Preis (Gleitkommazahl)
- Zeile 3: Anfangsmenge (Ganzzahl)
- Zeile 4: Zu setzende neue Menge (Ganzzahl)
Ausgabeformat:
Initial: [Name] x[Quantity] = $[Total]
After value update: [Name] x[Quantity] = $[Total]
After pointer update: [Name] x[Quantity] = $[Total]Bei den Eingaben Laptop, 999.99, 1 und 3 sollte deine Ausgabe beispielsweise folgendermaßen aussehen:
Initial: Laptop x1 = $999.99
After value update: Laptop x1 = $999.99
After pointer update: Laptop x3 = $2999.97Beachte, dass die Menge nach der Aktualisierung per Wert bei 1 bleibt (die Funktion hat eine Kopie geändert), sich nach der Aktualisierung per Zeiger jedoch auf 3 ändert (die Funktion hat das Original geändert). Formatiere die Gesamtsummen mit zwei Dezimalstellen.
Probier es selbst
package main
import (
"fmt"
)
func main() {
// Eingabe lesen
var name string
var price float64
var initialQty, newQty int
fmt.Scanln(&name)
fmt.Scanln(&price)
fmt.Scanln(&initialQty)
fmt.Scanln(&newQty)
// TODO: Erstelle ein CartItem mit den Eingabewerten
// TODO: Gib den Anfangszustand mit folgendem Format aus:
// fmt.Printf("Initial: %s x%d = $%.2f\n", ...)
// TODO: Versuche, die Menge mit UpdateQuantityByValue zu aktualisieren
// Gib dann das Ergebnis aus (beachte, dass sich die Menge nicht ändern wird)
// TODO: Aktualisiere die Menge mit UpdateQuantityByPointer
// Gib dann das Ergebnis aus (beachte, dass sich die Menge ändern wird)
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler