Was eine Klasse ist
Eine Klasse ist ein Bauplan für einen eigenen Typ, der Daten (Membervariablen) und Verhalten (Memberfunktionen) zu einer Einheit bündelt. Während eingebaute Typen wie int und double einzelne Werte beschreiben, kannst du mit einer Klasse ein ganzes Konzept - ein Bankkonto, einen 2D-Punkt, einen Spieler - als einen einzigen Wert modellieren, den du herumreichen kannst.
Du hast bereits Klassen verwendet, ohne selbst eine zu definieren: std::string und std::vector sind Klassen aus der Standardbibliothek. name.length() oder v.push_back(3) aufzurufen bedeutet, eine Memberfunktion auf einem Objekt aufzurufen. Jetzt baust du auf dieselbe Weise deine eigenen Typen und gehst danach zu den Konstruktoren über, um sie zu initialisieren.
Eine Klasse definieren und Objekte erstellen
Eine Klassendefinition listet ihre Member zwischen geschweiften Klammern auf und endet mit einem Semikolon - das abschließende ; zu vergessen, ist einer der häufigsten Anfängerfehler. Jedes Objekt, das du aus der Klasse erstellst, erhält seine eigene Kopie der Membervariablen.
rex und luna sind zwei getrennte Objekte. rex.name zu ändern berührt luna.name nicht - jedes Objekt hält seine eigenen Daten. Auf einen Member greifst du mit dem Punktoperator . auf einem Objekt zu (oder mit ->, wenn du einen Zeiger auf eines hast).
public vs. private
Standardmäßig ist alles in einer class private: Nur die Memberfunktionen der Klasse selbst dürfen darauf zugreifen. Das Label public: öffnet Member für externen Code. Diese Trennung ist das Herzstück der Kapselung - du stellst eine sichere Schnittstelle bereit und verbirgst die internen Daten, sodass sie nicht in einen ungültigen Zustand geraten können.
Da count private ist, kann kein Aufrufer einen negativen oder unsinnigen Wert einschleusen - er muss über increment() gehen. Das const nach der Parameterliste von value() sagt zu, dass die Funktion das Objekt nicht verändert, was den Aufruf auf const-Objekten erlaubt und Lesern die Absicht signalisiert. Die Zugriffsspezifizierer erklären die ganze Geschichte zu public, private und protected.
Deklaration vs. Definition von Methoden
Für kleine Klassen ist es in Ordnung, Methoden inline zu definieren (wie oben). Bei größeren Klassen ist es üblich, Methoden innerhalb der Klasse zu deklarieren und ihre Rümpfe außerhalb mit der Bereichssyntax ClassName::method zu definieren. So bleibt die Klassendefinition als Überblick über die Schnittstelle lesbar.
Das Präfix Rectangle:: sagt dem Compiler, zu welcher Klasse die Funktion gehört. Innerhalb einer solchen Definition beziehst du dich weiterhin mit ihren bloßen Namen (width, area()) auf die Member - der Compiler weiß, dass sie zu dem Objekt gehören, auf dem die Methode aufgerufen wurde.
Der this-Zeiger
Innerhalb jeder nicht-statischen Memberfunktion ist this ein Zeiger auf das Objekt, auf dem die Funktion aufgerufen wurde. Meist brauchst du ihn nicht, da ein bloßer Membername bereits auf das aktuelle Objekt verweist. Er wird nützlich, wenn ein Parameter einen Member überdeckt (shadow) - sie teilen sich einen Namen - und du die Mehrdeutigkeit auflösen musst.
Ohne this-> würde x = x; innerhalb von setX den Parameter sich selbst zuweisen und den Member unberührt lassen - ein stiller Fehler. this->x macht es eindeutig. Viele Codebasen vermeiden das Problem ganz, indem sie Parameter anders benennen (z. B. setX(int newX)), aber in echtem Code wirst du this-> ständig sehen.
Häufige Fehler
Eine Handvoll Klassen-Fallen erwischt Leute immer wieder:
- Das schließende Semikolon vergessen. Eine Klassendefinition endet mit
};. Lass das;weg, und du bekommst eine Flut verwirrender Fehler, die auf das zeigen, was nach der Klasse kommt, nicht auf die Klasse selbst. - Member nicht initialisieren. Member eingebauter Typen wie
intunddoublewerden nicht automatisch auf null gesetzt. Einen nicht initialisierten Member zu lesen ist undefiniertes Verhalten. Gib Membern Standardwerte (int count = 0;) oder initialisiere sie in einem Konstruktor. - Von außen auf private Member zugreifen.
c.count = 5;auf einem private Member ist ein Kompilierfehler - gehe stattdessen über eine öffentliche Methode. Das ist die Kapselung, die wie vorgesehen funktioniert. - Die Klasse mit einem Objekt verwechseln.
Dog.bark();ist falsch -Dogist der Typ. Methoden rufst du auf Objekten auf:rex.bark();.
// Nicht initialisierter Member - 'age' zu lesen ist undefiniertes Verhalten:
class Cat {
public:
int age; // kein Standardwert
};
Cat c;
std::cout << c.age; // Müllwert, nicht 0
Als Nächstes: Konstruktoren
Jeden Member nach dem Erstellen eines Objekts von Hand zu setzen - rex.name = ...; rex.age = ...; - ist mühsam und leicht zu vergessen, und genau so landest du bei nicht initialisierten Membern. Die nächste Seite behandelt Konstruktoren: spezielle Funktionen, die beim Erstellen eines Objekts automatisch laufen und mit denen du garantieren kannst, dass jedes Objekt mit einer sauberen einzeiligen Syntax Dog rex("Rex", 4); in einem gültigen Zustand startet.
Häufig gestellte Fragen
Was ist der Unterschied zwischen einer Klasse und einem Objekt in C++?
Eine Klasse ist der Bauplan - sie beschreibt, welche Daten (Membervariablen) und welches Verhalten (Memberfunktionen) ein Typ besitzt. Ein Objekt ist eine konkrete Instanz, die aus diesem Bauplan erzeugt wird. class Dog { ... }; definiert den Typ einmalig; Dog rex; erstellt einen echten Dog, den du verwenden kannst. Eine Klasse kann viele voneinander unabhängige Objekte erzeugen.
Was ist der Unterschied zwischen class und struct in C++?
Technisch nur die voreingestellte Zugriffsebene: Member einer class sind standardmäßig private, Member einer struct standardmäßig public. Beide können Memberfunktionen, Konstruktoren und Vererbung haben. Per Konvention wird struct für reine Datenbündel und class für Typen mit Verhalten und zu schützenden Invarianten verwendet.
Was macht der this-Zeiger in einer C++-Klasse?
Innerhalb einer Memberfunktion ist this ein Zeiger auf das Objekt, auf dem die Funktion aufgerufen wurde. Verwende ihn, um einen Parameter von einem Member zu unterscheiden (this->x = x;) oder um das aktuelle Objekt zurückzugeben. Für den normalen Memberzugriff brauchst du ihn selten - x zu schreiben bezieht sich bereits auf das x des aktuellen Objekts.