Programming lesson
Zeiger und Operatorüberladung in C++: Ein Tutorial für das ContactBook-Projekt (Mai 2026)
Lerne, wie du mit Zeigern und Operatorüberladung in C++ ein effizientes Kontaktbuch erstellst – inspiriert von aktuellen Trends wie KI-gestützten Adressbüchern und Speicheroptimierung in Apps.
Einleitung: Warum Zeiger und Operatorüberladung wichtig sind
Stell dir vor, du entwickelst eine App wie die neueste KI-gestützte Kontaktverwaltung, die auf deinem Smartphone und Laptop läuft. Jedes Gerät speichert eigene Kopien der Kontakte – das kostet Speicherplatz. Mit Zeigern in C++ kannst du Speicher sparen, indem du nur einmal pro Kontakt Speicher reservierst und von beiden Geräten darauf verweist. Die Operatorüberladung macht die Handhabung intuitiv: Statt umständlicher Methodenaufrufe schreibst du einfach buch1 += kontakt. In diesem Tutorial lernst du beides anhand eines ContactBook-Projekts – ähnlich wie bei COP3503C Lab 3.
Grundlagen: Arrays, Zeiger und Referenzen
Array-Deklaration und -Initialisierung
Ein Array ist eine Sammlung von Elementen gleichen Typs. In C++ muss die Größe zur Compile-Zeit bekannt sein. Beispiel:
const int MAX = 100;
int zahlen[MAX] = {0};Der Zugriff erfolgt über den Index: zahlen[0] = 42;
Zeiger (Pointer)
Ein Zeiger speichert die Adresse einer Variable. Deklaration mit *:
int wert = 10;
int* ptr = &wert; // ptr zeigt auf wert
cout << *ptr; // Dereferenzierung: gibt 10 ausMit nullptr initialisieren, wenn kein gültiges Ziel vorhanden ist:
int* ptr = nullptr;Der Pfeiloperator -> greift auf Member eines Objekts über einen Zeiger zu:
class Kontakt { public: string name; };
Kontakt* k = new Kontakt();
k->name = "Max";Referenzen
Referenzen sind Alternativnamen für Variablen. Sie vermeiden Kopien und erlauben direkte Modifikation:
void aendere(int& x) { x = 5; }
int a = 3;
aendere(a); // a ist jetzt 5Rückgabe einer Referenz ermöglicht direkte Änderung des Rückgabewerts.
Das ContactBook-Projekt: Klassenstruktur
Das Projekt besteht aus zwei Klassen: Contact und ContactBook. Die ContactBook-Klasse speichert ein Array von Contact*-Zeigern. Dadurch teilen sich mehrere Geräte denselben Speicher für einen Kontakt.
Contact-Klasse
Enthält Name und Telefonnummer als string. Notwendige Methoden: Konstruktor, getName(), getNumber(), display().
class Contact {
private:
string name;
string nummer;
public:
Contact(string n, string nr) : name(n), nummer(nr) {}
string getName() const { return name; }
string getNumber() const { return nummer; }
void display() const {
cout << name << ", " << nummer << endl;
}
};ContactBook-Klasse
Enthält ein statisches Array von Contact* (Größe z.B. 100) und einen Zähler für die aktuelle Anzahl. Methoden: find, add, addContacts, display, alphabetize, sowie überladene Operatoren.
class ContactBook {
private:
static const int MAX = 100;
Contact* kontakte[MAX];
int anzahl;
public:
ContactBook() : anzahl(0) {}
// Weitere Methoden...
};Wichtig: Die Klassen sind unabhängig, Contact wird nicht innerhalb von ContactBook definiert.
Operatorüberladung: Intuitive Syntax
Operatorüberladung erlaubt es, Operatoren wie +, +=, - für eigene Klassen zu definieren. Das macht den Code lesbarer. Im ContactBook-Projekt werden folgende Operatoren überladen:
+= Contact– Einen Kontakt hinzufügen+= ContactBook– Alle Kontakte eines anderen Buches hinzufügen+ ContactBook– Zwei Bücher zusammenführen (neues Buch)-= Contact– Einen bestimmten Kontakt entfernen-= ContactBook– Alle Kontakte eines anderen Buches entfernen- ContactBook– Differenz zweier Bücher (neues Buch)
Beispiel für += mit einem Kontakt:
ContactBook& operator+=(const Contact& c) {
kontakte[anzahl++] = new Contact(c); // Kopie erstellen
return *this;
}Beachte: Wir erstellen eine Kopie des Kontakts, da c als Referenz übergeben wird. Der Zeiger im Array zeigt auf die Kopie.
Für += mit einem anderen ContactBook:
ContactBook& operator+=(const ContactBook& other) {
for (int i = 0; i < other.anzahl; ++i) {
kontakte[anzahl++] = new Contact(*other.kontakte[i]);
}
return *this;
}Der +-Operator erzeugt ein neues Buch:
ContactBook operator+(const ContactBook& other) const {
ContactBook ergebnis = *this;
ergebnis += other;
return ergebnis;
}Ähnlich funktionieren die Minus-Operatoren, wobei find verwendet wird, um den zu entfernenden Kontakt zu lokalisieren.
Speicherverwaltung mit Zeigern
Jedes new benötigt ein delete. Im Destruktor der ContactBook-Klasse müssen alle dynamisch angelegten Kontakte gelöscht werden:
~ContactBook() {
for (int i = 0; i < anzahl; ++i)
delete kontakte[i];
}Auch Kopierkonstruktor und Zuweisungsoperator müssen tiefe Kopien erstellen, um doppelte Löschungen zu vermeiden.
Praktische Anwendung: Speicher sparen wie in einer Cloud-App
Stell dir vor, du hast eine Cloud-basierte Kontakt-App, die auf mehreren Geräten läuft. Ohne Zeiger würde jedes Gerät eine eigene Kopie jedes Kontakts speichern – bei 1000 Kontakten à 1 KB sind das 1 MB pro Gerät. Mit Zeigern teilen sich alle Geräte denselben Speicher: nur 1 KB pro Kontakt insgesamt. Das spart Speicher und vereinfacht Synchronisation. Dieses Prinzip wird in vielen modernen Apps verwendet, z.B. in KI-gestützten Adressbüchern, die Kontakte aus verschiedenen Quellen zusammenführen.
Häufige Fehler und wie du sie vermeidest
- Vergessen von
delete: Führt zu Speicherlecks. Verwende einen Destruktor. - Flache Kopie: Standardmäßig kopiert der Kopierkonstruktor nur die Zeiger, nicht die Objekte. Implementiere eine tiefe Kopie.
- Zeiger auf lokale Variable: Wenn du die Adresse einer lokalen Variable zurückgibst, wird sie nach Verlassen des Gültigkeitsbereichs ungültig.
- Falsche Verwendung von
newunddelete: Jedesnewmuss genau eindeletehaben.
Fazit
Mit Zeigern und Operatorüberladung hast du mächtige Werkzeuge, um effiziente und intuitive C++-Programme zu schreiben. Das ContactBook-Projekt zeigt, wie du Speicher sparst und gleichzeitig eine benutzerfreundliche Schnittstelle bereitstellst. Probiere es selbst aus – und denk daran: Übung macht den Meister. Viel Erfolg bei deinem Lab 3!