Warum Daten zusammenfassen
Bisher stand jede Variable für sich allein: hier ein int, dort ein string. Doch echte Programme haben es mit Dingen zu tun, die aus mehreren Teilen bestehen – ein Punkt hat ein x und ein y, ein Student hat einen Namen, ein Alter und einen Notendurchschnitt. Solche Werte als lose, getrennte Variablen herumzureichen ist fehleranfällig; nichts verbindet sie, und eine Funktion, die alle drei braucht, muss alle drei Parameter entgegennehmen.
Ein Struct löst das. Er definiert einen neuen Typ, der zusammengehörige Variablen – seine Member – zu einer Einheit bündelt. Einmal definiert, behandelst du das gesamte Bündel als einen einzigen Wert, den du speichern, kopieren und an Funktionen übergeben kannst.
Auf jeden Member greifst du mit dem Punktoperator zu (s.name, s.age). Das Semikolon nach der schließenden } einer Struct-Definition ist Pflicht – es wegzulassen ist einer der häufigsten Kompilierfehler von C++-Anfängern.
Einen Struct initialisieren
Jedes Feld von Hand zuzuweisen funktioniert, ist aber umständlich, und man vergisst leicht einen Member. Die sauberere Variante ist die Aggregat-Initialisierung: Liste die Werte in geschweiften Klammern auf, in derselben Reihenfolge, in der die Member deklariert wurden.
Vorsicht beim leeren Standardfall: Point p; (ohne Klammern) lässt x und y mit Müllwerten zurück, weil Member eingebauter Typen nicht automatisch genullt werden. Point p{}; wertinitialisiert sie auf 0. Bevorzuge die Klammern. Du kannst Standardwerte auch direkt in die Definition einbauen, sodass selbst Point p; sauber startet:
Structs als Funktionsparameter
Ein Struct ist ein einziger Wert, daher kann eine Funktion einen Parameter statt drei entgegennehmen. Denk nur daran, dass das Übergeben eines Structs per Wert alle Member kopiert. Für alles, was größer als ein paar ints ist, übergib per const-Referenz, um die Kopie zu sparen – dieselbe Regel, die du auf der Seite zu Referenzen gesehen hast.
Einen ganzen Struct aus einer Funktion zurückzugeben ist die idiomatische Art, mehrere Werte auf einmal zurückzuliefern – weit sauberer, als mit mehreren Ausgabe-Referenzen zu jonglieren.
Member-Funktionen und Konstruktoren hinzufügen
Ein Struct beschränkt sich nicht auf Daten. Er kann Member-Funktionen enthalten, die auf seinen eigenen Membern arbeiten, und einen Konstruktor, der das Objekt im Moment seiner Erzeugung initialisiert. Hier beginnt ein Struct, wie ein kleines Objekt mit Verhalten auszusehen.
Innerhalb einer Member-Funktion wird auf die Member über ihren Namen zugegriffen (width, height) – sie beziehen sich auf die Kopie dieses Objekts. area() const zu markieren teilt dem Compiler mit, dass die Funktion das Objekt nur liest, wodurch du sie auch auf const Rectangle-Werten aufrufen kannst. Konstruktoren sind ein Thema für sich, einschließlich der Initialisiererlisten-Syntax : width(w) – die Seite zu Konstruktoren geht in die Tiefe.
Struct vs. class: der wahre Unterschied
Du hast vielleicht gehört, dass „Structs für Daten und Klassen für Objekte da sind". Das ist eine Konvention, keine Sprachregel. In C++ sind ein struct und eine class fast dasselbe – der einzige eingebaute Unterschied ist die Standard-Zugriffsebene.
struct S {
int x; // standardmäßig public
};
class C {
int x; // standardmäßig private
};
Member eines struct sind public, sofern du sie nicht anders markierst; die einer class starten als private. Das war's – beide können Konstruktoren, Member-Funktionen, Vererbung und alles andere haben. Du kannst einem Struct sogar explizite Zugriffsspezifizierer hinzufügen, um Member zu verbergen:
Die praktische Erkenntnis: Verwende struct für transparente Datenbündel, bei denen jedes Feld frei angefasst werden soll (ein Point, eine Color, ein Konfigurationsdatensatz), und greife zu class, wenn du den internen Zustand hinter einer öffentlichen Schnittstelle schützen willst. Der Compiler behandelt sie gleich; das Schlüsselwort signalisiert nur deine Absicht.
Arrays und Vectors von Structs
Da ein Struct ein gewöhnlicher Typ ist, kannst du viele davon in ein Array oder einen Vector packen und wie über jeden anderen Wert darüber iterieren.
Beachte die verschachtelten Klammern: Jedes {"Keyboard", 49.99} aggregat-initialisiert ein Product, und die äußeren Klammern bauen den Vector. const Product& in der bereichsbasierten Schleife zu verwenden vermeidet das Kopieren jedes Structs in jeder Iteration – lässt du das & weg, würdest du jedes Element unnötig duplizieren.
Als Nächstes: Enums
Mit Structs baust du einen Typ aus mehreren zusammengefassten Werten. Das nächste Kapitel geht in die umgekehrte Richtung: Ein Enum definiert einen Typ, der genau einen Wert aus einer kleinen, benannten Menge halten kann – perfekt für Dinge wie Color::Red, Direction::North oder den Zustand eines Zustandsautomaten. Du wirst sehen, wie enum class dir lesbare, typsichere Konstanten gibt, die sich natürlich mit den Structs und Klassen kombinieren lassen, die du jetzt baust.
Häufig gestellte Fragen
Was ist ein Struct in C++?
Ein struct ist ein benutzerdefinierter Typ, der mehrere zusammengehörige Variablen (genannt Member) unter einem Namen bündelt. Statt getrennte Variablen string name, int age und double gpa zu jonglieren, fasst du sie zu einem einzigen Typ Student zusammen und reichst dieses eine Objekt herum. Ein Struct kann auch Member-Funktionen und Konstruktoren enthalten – in modernem C++ ist er eine vollwertige Klasse, deren Member standardmäßig public sind.
Was ist der Unterschied zwischen struct und class in C++?
Technisch nur die Standard-Zugriffsebene: Member eines struct sind public, sofern du nichts anderes angibst, während Member einer class standardmäßig private sind. Dasselbe gilt für die Vererbung. Alles andere – Konstruktoren, Member-Funktionen, Methoden, Vererbung – funktioniert identisch. Per Konvention greifen Programmierer zu struct für einfache Datenbündel und zu class für Typen mit Invarianten und verborgenen Interna.
Wie initialisiert man einen Struct in C++?
Am einfachsten geht es mit der Aggregat-Initialisierung in geschweiften Klammern in Member-Reihenfolge: Point p{3, 4};. Du kannst auch jedes Feld einzeln mit dem Punktoperator zuweisen (p.x = 3;), den Membern in der Definition Standardwerte geben (int x = 0;) oder einen Konstruktor schreiben, damit sich der Struct selbst einrichtet. Die Klammer-Initialisierung ist vorzuziehen, weil sie keine Felder stillschweigend uninitialisiert lässt.