Zeiger auf Structs
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 20 von 107.
Bei der Arbeit mit Strukturen verwendest du häufig Zeiger, um das Kopieren großer Datenstrukturen zu vermeiden und die ursprüngliche Struktur zu ändern. Ein Zeiger auf eine Struktur enthält die Speicheradresse dieser Strukturinstanz.
Du kannst einen Struct-Zeiger mit dem Operator & auf einer vorhandenen Struktur erstellen 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 bestehende struct
// Oder direkt erstellen
ptr2 := &Person{Name: "Bob", Age: 25}Go bietet eine praktische Abkürzung beim Zugriff auf Struct-Felder über einen Zeiger. Anstatt (*ptr).Name zu schreiben, kannst du einfach ptr.Name schreiben. Go dereferenziert den Zeiger automatisch für dich.
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 Zeiger wirken sich auf die ursprüngliche Struktur aus.
p := Person{Name: "Alice", Age: 30}
ptr := &p
ptr.Age = 31 // verändert das Original
fmt.Println(p.Age) // 31Struct-Zeiger sind in den OOP-Mustern von Go unverzichtbar. Sie ermöglichen es Methoden, den Zustand des Empfängers 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
EinfachErstellen wir ein Bankkontosystem, das veranschaulicht, wie Strukturzeiger es ermöglichen, die ursprünglichen Daten effizient zu ändern. Du erstellst ein Konto, das durch Zeigeroperationen aktualisiert werden kann.
Du organisierst deinen Code auf zwei Dateien verteilt:
account.go: Definiere eineBankAccount-Struktur mit den FeldernOwner(string),Balance(float64) undAccountNumber(string). Erstelle eine Funktion namensNewAccount, die einen Namen des Kontoinhabers und eine Kontonummer als Parameter entgegennimmt und einen Zeiger auf eine neueBankAccountmit einem Anfangsguthaben von 0 zurückgibt. Erstelle außerdem eineDeposit-Funktion, die einen*BankAccount-Zeiger und einen Betrag entgegennimmt und diesen Betrag anschließend über den Zeiger zum Kontostand addiert.main.go: Lies die Kontoinformationen aus der Eingabe ein, erstelle ein Konto mithilfe deiner Konstruktorfunktion (die einen Zeiger zurückgibt), zahle mithilfe des Zeigers einen Betrag ein und zeige die Kontodetails an, um zu bestätigen, dass der Kontostand geändert wurde.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Name des Kontoinhabers
- Zeile 2: Kontonummer
- Zeile 3: Einzahlungsbetrag (float)
Ausgabeformat:
Account: [AccountNumber]
Owner: [Owner]
Balance: [Balance]Wenn beispielsweise John Smith, ACC-12345 und 500.50 angegeben werden, sollte deine Ausgabe folgendermaßen aussehen:
Account: ACC-12345
Owner: John Smith
Balance: 500.50Denke daran, dass deine NewAccount-Funktion mithilfe des *BankAccount-Operators & zurückgeben sollte und deine Deposit-Funktion den Kontostand direkt über den Zeiger mithilfe der automatischen Dereferenzierung von Go ändern kann (z. B. account.Balance statt (*account).Balance).
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 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