Kurz: Eine Klasse bündelt Daten (Member-Variablen) und Funktionen (Methoden). Von ihr erzeugst du Objekte.
Teil des Kurses C++
Eine Klasse bündelt Daten (Member-Variablen) und Funktionen (Methoden). Von ihr erzeugst du Objekte.
#include <iostream>
#include <string>
class Konto {
public:
Konto(std::string inhaber, double startwert = 0)
: inhaber_(std::move(inhaber)), stand_(startwert) {} // Initialisierungsliste
void einzahlen(double betrag) {
if (betrag <= 0) throw std::invalid_argument("Betrag muss positiv sein");
stand_ += betrag;
}
bool abheben(double betrag) {
if (betrag > stand_) return false;
stand_ -= betrag;
return true;
}
double stand() const { return stand_; } // const: ändert das Objekt nicht
const std::string& inhaber() const { return inhaber_; }
private:
std::string inhaber_;
double stand_;
};
int main() {
Konto k("Mia", 100);
k.einzahlen(50);
std::cout << k.inhaber() << ": " << k.stand() << '\n';
std::cout << k.abheben(30) << ' ' << k.abheben(1000) << ' ' << k.stand() << '\n';
try { k.einzahlen(-5); } catch (const std::exception& e) { std::cout << "Fehler: " << e.what() << '\n'; }
return 0;
}Ausgabe:
Mia: 150
1 0 120
Fehler: Betrag muss positiv seinpublic/private/protectedsteuern die Sichtbarkeit. Beiclassist alles standardmäßig privat, beistructöffentlich- Der Konstruktor hat den Namen der Klasse; die Initialisierungsliste (
: a_(a), b_(b)) initialisiert Member direkt consthinter der Methode: Sie verändert das Objekt nicht und ist auch fürconst-Objekte aufrufbar- Üblich: Member mit Unterstrich (
stand_) kennzeichnen
Destruktor und RAII
Der Destruktor (~Klasse()) läuft automatisch, wenn das Objekt ungültig wird. Hier gibst du Ressourcen frei:
#include <iostream>
#include <string>
class Datei {
public:
explicit Datei(std::string name) : name_(std::move(name)) { std::cout << "öffne " << name_ << '\n'; }
~Datei() { std::cout << "schließe " << name_ << '\n'; }
Datei(const Datei&) = delete; // nicht kopierbar
Datei& operator=(const Datei&) = delete;
private:
std::string name_;
};
int main() {
Datei a("a.txt");
{
Datei b("b.txt");
std::cout << "im Block\n";
} // b wird hier automatisch geschlossen
std::cout << "ende von main\n";
return 0;
} // a wird automatisch geschlossenAusgabe:
öffne a.txt
öffne b.txt
im Block
schließe b.txt
ende von main
schließe a.txtDieses Muster heißt RAII (Resource Acquisition Is Initialization): Ressourcen (Speicher, Dateien, Sperren, Verbindungen) gehören einem Objekt, das sie im Konstruktor erwirbt und im Destruktor freigibt. Dadurch kann nichts vergessen werden, auch bei Ausnahmen.
Kopieren und Verschieben (Rule of Zero / Five)
Hat eine Klasse nur Member, die sich selbst verwalten (std::string, std::vector, Smart Pointer), brauchst du nichts zu schreiben. Der Compiler erzeugt korrektes Kopieren, Verschieben und Zerstören (Rule of Zero). Nur wer eine Ressource roh verwaltet, muss alle fünf Spezialfunktionen selbst festlegen: Destruktor, Kopierkonstruktor, Kopierzuweisung, Move-Konstruktor, Move-Zuweisung (Rule of Five).
#include <iostream>
#include <string>
#include <vector>
#include <utility>
struct Spieler {
std::string name;
std::vector<int> punkte;
};
int main() {
Spieler a{"Mia", {10, 20, 30}};
Spieler b = a; // Kopie: alles wird dupliziert
b.punkte.push_back(40);
Spieler c = std::move(a); // Verschieben: Inhalt wandert, a ist danach leer
std::cout << a.punkte.size() << ' ' << b.punkte.size() << ' ' << c.punkte.size() << ' ' << c.name << '\n';
return 0;
}Ausgabe:
0 4 3 Miastd::move markiert ein Objekt als “darf ausgeräumt werden”. Danach solltest du es nur noch zuweisen oder zerstören.
static, this, friend
#include <iostream>
class Zaehler {
public:
Zaehler() { ++anzahl_; }
~Zaehler() { --anzahl_; }
static int anzahl() { return anzahl_; } // gehört der Klasse
Zaehler& erhoehe() { ++wert_; return *this; } // this: Zeiger auf das aktuelle Objekt
int wert() const { return wert_; }
private:
static inline int anzahl_ = 0; // inline: Definition direkt in der Klasse (C++17)
int wert_ = 0;
};
int main() {
Zaehler a;
{
Zaehler b;
std::cout << Zaehler::anzahl() << '\n';
}
std::cout << Zaehler::anzahl() << ' ' << a.erhoehe().erhoehe().wert() << '\n';
return 0;
}Ausgabe:
2
1 2Operatoren überladen
Eigene Klassen können sich wie eingebaute Typen verhalten:
#include <iostream>
#include <compare>
struct Vektor {
double x = 0, y = 0;
Vektor operator+(const Vektor& o) const { return {x + o.x, y + o.y}; }
Vektor operator*(double k) const { return {x * k, y * k}; }
bool operator==(const Vektor&) const = default; // automatisch (C++20)
friend std::ostream& operator<<(std::ostream& os, const Vektor& v) { return os << '(' << v.x << ", " << v.y << ')'; }
};
int main() {
Vektor a{1, 2}, b{3, 4};
std::cout << a + b << ' ' << a * 3 << ' ' << (a == Vektor{1, 2}) << '\n';
return 0;
}Ausgabe:
(4, 6) (3, 6) 1friend gibt einer Funktion Zugriff auf private Member. Nutze es sparsam.
struct oder class?
Faustregel: struct für reine Daten ohne Regeln (alles öffentlich), class für Typen mit Invarianten (Regeln, die immer gelten müssen) und privaten Daten.
Merke
- Klasse = Daten + Methoden;
public/private; Konstruktor mit Initialisierungsliste const-Methoden ändern das Objekt nicht- Destruktoren und RAII geben Ressourcen automatisch frei
- Rule of Zero: nur Member verwenden, die sich selbst verwalten
std::moveverschiebt statt zu kopieren- Operatoren lassen sich überladen (
+,==,<<)
Übungsaufgabe
Schreibe eine Klasse Bruch mit Zähler und Nenner (gekürzt), operator+, operator* und operator<<.
Quiz zur Selbstkontrolle
Wann läuft der Destruktor?
- Automatisch, wenn das Objekt ungültig wird (Blockende, delete) (richtig)
- Nur wenn man ihn aufruft
- Beim Programmstart
- Beim Kopieren
Was bedeutet const hinter einer Methode?
- Die Methode ändert das Objekt nicht (richtig)
- Die Methode ist schneller
- Die Methode ist statisch
- Die Methode ist privat
Was besagt die Rule of Zero?
- Verwende selbstverwaltende Member, dann sind keine Spezialfunktionen nötig (richtig)
- Schreibe keine Klassen
- Setze alle Variablen auf 0
- Verwende keine Destruktoren
Weiter im Kurs
Zurück: Strings und Container der Standardbibliothek
Weiter: Vererbung und Polymorphie
Alle Kapitel: C++ im Überblick