Zeiger auf Structs
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 20 von 107.
Beim Arbeiten mit Structs werden Sie oft Pointer verwenden, um das Kopieren großer Datenstrukturen zu vermeiden und um das ursprüngliche Struct zu verändern. Ein Pointer auf ein Struct hält die Speicheradresse dieser Struct-Instanz.
Sie können einen Struct-Pointer erstellen, indem Sie den &-Operator auf ein bestehendes Struct anwenden oder den Adressoperator direkt mit einem Struct-Literal verwenden.
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
ptr := &p // Zeiger auf ein bestehendes Struct
// Oder direkt erstellen
ptr2 := &Person{Name: "Bob", Age: 25}Go bietet eine praktische Abkürzung beim Zugriff auf Struct-Felder über einen Pointer. Anstatt (*ptr).Name zu schreiben, können Sie einfach ptr.Name schreiben. Go dereferenziert den Pointer automatisch für Sie.
ptr := &Person{Name: "Alice", Age: 30}
// Beide funktionieren identisch
fmt.Println((*ptr).Name) // explizite Dereferenzierung
fmt.Println(ptr.Name) // automatische DereferenzierungDiese automatische Dereferenzierung gilt auch beim Ändern von Feldern. Änderungen über den Pointer wirken sich auf das ursprüngliche Struct aus.
p := Person{Name: "Alice", Age: 30}
ptr := &p
ptr.Age = 31 // verändert das Original
fmt.Println(p.Age) // 31Struct-Pointer sind in den OOP-Mustern von Go essenziell. Sie ermöglichen es Methoden, den Zustand des Receivers zu ändern, erlauben die effiziente Übergabe großer Structs an Funktionen und bilden die Grundlage für den Aufbau verknüpfter Datenstrukturen wie Bäume und Listen.
Aufgabe
EinfachLassen Sie uns ein Bankkontosystem bauen, das demonstriert, wie Struct-Pointer es Ihnen ermöglichen, die ursprünglichen Daten effizient zu modifizieren. Sie werden ein Konto erstellen, das durch Pointer-Operationen aktualisiert werden kann.
Sie werden Ihren Code in zwei Dateien organisieren:
account.go: Definieren Sie einBankAccount-Struct mit den FeldernOwner(string),Balance(float64) undAccountNumber(string). Erstellen Sie eine Funktion namensNewAccount, die einen Besitzernamen und eine Kontonummer als Parameter entgegennimmt und einen Pointer auf ein neuesBankAccountmit einem Startguthaben von 0 zurückgibt. Erstellen Sie außerdem eineDeposit-Funktion, die einen*BankAccount-Pointer und einen Betrag entgegennimmt und diesen Betrag dann über den Pointer zum Guthaben des Kontos addiert.main.go: Lesen Sie die Kontoinformationen aus der Eingabe ein, erstellen Sie ein Konto mit Ihrer Konstruktorfunktion (die einen Pointer zurückgibt), tätigen Sie eine Einzahlung unter Verwendung des Pointers und zeigen Sie die Kontodetails an, um zu bestätigen, dass das Guthaben geändert wurde.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Besitzername
- Zeile 2: Kontonummer
- Zeile 3: Einzahlungsbetrag (float)
Ausgabeformat:
Account: [AccountNumber]
Owner: [Owner]
Balance: [Balance]Zum Beispiel, gegeben John Smith, ACC-12345 und 500.50, sollte Ihre Ausgabe wie folgt aussehen:
Account: ACC-12345
Owner: John Smith
Balance: 500.50Denken Sie daran, dass Ihre NewAccount-Funktion *BankAccount unter Verwendung des &-Operators zurückgeben sollte und Ihre Deposit-Funktion das Guthaben direkt über den Pointer modifizieren kann, indem sie Go's automatische Dereferenzierung nutzt (z. B. account.Balance anstelle von (*account).Balance).
Spickzettel
Ein Pointer auf ein Struct hält die Speicheradresse einer Struct-Instanz. Erstellen Sie Struct-Pointer mit dem &-Operator:
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
ptr := &p // Pointer auf ein existierendes Struct
// Oder direkt erstellen
ptr2 := &Person{Name: "Bob", Age: 25}Go bietet eine automatische Dereferenzierung beim Zugriff auf Struct-Felder über einen Pointer. Anstatt (*ptr).Name können Sie ptr.Name schreiben:
ptr := &Person{Name: "Alice", Age: 30}
// Beides funktioniert identisch
fmt.Println((*ptr).Name) // explizite Dereferenzierung
fmt.Println(ptr.Name) // automatische DereferenzierungÄnderungen über einen Pointer wirken sich auf das ursprüngliche Struct aus:
p := Person{Name: "Alice", Age: 30}
ptr := &p
ptr.Age = 31 // modifiziert das Original
fmt.Println(p.Age) // 31Struct-Pointer ermöglichen es Methoden, den Zustand des Receivers zu ändern, erlauben die effiziente Übergabe großer Structs an Funktionen und bilden die Grundlage für verkettete Datenstrukturen.
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Name des Inhabers lesen
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Kontonummer lesen
accountNumber, _ := reader.ReadString('\n')
accountNumber = strings.TrimSpace(accountNumber)
// Einzahlungsbetrag lesen
amountStr, _ := reader.ReadString('\n')
amountStr = strings.TrimSpace(amountStr)
amount, _ := strconv.ParseFloat(amountStr, 64)
// TODO: Erstelle ein neues Konto mit der Funktion NewAccount (gibt einen Pointer zurück)
// TODO: Führe eine Einzahlung mit der Funktion Deposit und dem Konto-Pointer durch
// TODO: Gib die Kontodetails im erforderlichen Format aus:
// Account: [AccountNumber]
// Owner: [Owner]
// Balance: [Balance]
}
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 Go OOP
Externe DateienGo Workspace & ModulePackages & ImportsExportierte vs. nicht-exportierte NamenEinführung in OOP in GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktor-FunktionenZusammenfassung – Einfacher Taschenrechner4Interfaces
Einführung in InterfacesImplizite ImplementierungInterface als VertragLeeres Interface (any)Type AssertionType SwitchInterface-KompositionStringer & Error InterfacesRückblick - Formen-Rechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf Paket-EbeneGetter- & Setter-MethodenInformation Hiding in GoZusammenfassung - Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterType ConstraintsGenerische StructsWorkaround für generische MethodenZusammenfassung – Generische Collection2Vertiefung: Typen & Structs
Basis- & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct TagsAnonyme StructsVerschachtelte StructsZero Values & StandardwerteZusammenfassung - Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen des Struct EmbeddingMethod PromotionEinbetten mehrerer StructsEmbedding vs. AggregationShadowing eingebetteter MethodenZusammenfassung – Mitarbeiter-Hierarchie8Fehlerbehandlung & OOP
Das error-InterfaceEigene FehlertypenError Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverZusammenfassung – Datei-Parser3Zeiger & Speicher
Zeiger-Grundlagen in GoZeiger auf StructsWertübergabe vs. ReferenzübergabeDie new() FunktionGarbage Collection in GoWiederholung – Linked List Builder6Polymorphismus in Go
Polymorphismus über InterfacesDuck Typing in GoInterface-ErfüllungsregelnPolymorphe CollectionsDependency InjectionZusammenfassung – Payment Processor9Nebenläufigkeit & OOP
Goroutines GrundlagenChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignZusammenfassung - Worker Pool