Warum Zeiger und nicht Objekt? C++ unter der Haube für Anfänger

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Die ewige Anfängerfrage

\n\n

Wenn Sie jemals auf Stack Overflow im C++-Bereich unterwegs waren, haben Sie sicherlich die Frage gesehen, die tausende von Aufrufen hat: „Warum sollte ich einen Zeiger und nicht das Objekt selbst verwenden?“. Und das ist keine müßige Neugier. Jeder, der nach Python oder JavaScript mit C++ beginnt, steht vor diesem Dilemma. Ein Objekt scheint doch praktisch: erstellt, Methode aufgerufen, vergessen. Wozu dann diese Sternchen, Pfeile und new?

\n\n

Tatsächlich liegt die Antwort darin, wie C++ den Speicher unter der Haube verwaltet. In diesem Artikel geben wir nicht nur eine trockene Definition, sondern analysieren reale Szenarien aus dem Programmiereralltag, in denen ein Zeiger nicht nur praktisch, sondern die einzig richtige Wahl ist. Bereit, einen Blick in den Motor Ihres Codes zu werfen? Los geht's!

\n\n

Objekt vs. Zeiger: Was sagt der Compiler?

\n\n

Kommen wir direkt zur Sache. Stellen Sie sich vor, Sie schreiben eine Klasse Monster für Ihr RPG-Spiel.

\n\n
class Monster {\npublic:\n    Monster() { std::cout << "Monster erstellt!\\n"; }\n    ~Monster() { std::cout << "Monster zerstört!\\n"; }\n    void roar() { std::cout << "RAAAWR!\\n"; }\n};
\n\n

Jetzt zwei Möglichkeiten, diese Klasse zu verwenden:

\n\n

1. Objekt auf dem Stack (automatische Verwaltung)

\n
void spawnOnStack() {\n    Monster m;  // Objekt lebt hier\n    m.roar();\n}  // Hier wird m automatisch zerstört
\n\n

2. Zeiger auf den Heap (manuelle Verwaltung)

\n
void spawnOnHeap() {\n    Monster* m = new Monster(); // Objekt im Heap\n    m->roar();\n    delete m;  // Müssen wir selbst löschen!\n}
\n\n

Die erste Variante scheint einfacher und sicherer. Wozu also die zweite? Unter der Haube verbirgt sich hier der entscheidende Unterschied: Stack und Heap.

\n\n
    \n
  • Stack — klein, schnell, aber das Objekt lebt nur innerhalb des Blocks {}.
  • \n
  • Heap — groß, langsamer, aber das Objekt lebt, bis Sie es löschen.
  • \n
\n\n

Wenn die Größe eines Objekts enorm ist (z. B. eine 4K-Textur), bedeutet die Speicherung auf dem Stack ein Risiko für einen Stack-Überlauf. Hier kommen die Zeiger ins Spiel.

\n\n

Top-5 Gründe, einen Zeiger zu verwenden (statt eines Objekts)

\n\n

Nachdem wir den grundlegenden Unterschied verstanden haben, analysieren wir konkrete Situationen, in denen ein Zeiger keine Laune, sondern eine Notwendigkeit ist.

\n\n

1. Polymorphie: Wenn der Typ nicht sofort bekannt ist

\n

Dies ist wohl der häufigste Grund. Angenommen, Sie haben eine Basisklasse Character und die Ableitungen Warrior, Mage. Wenn Sie ein Objekt auf dem Stack erstellen, müssen Sie den Typ genau angeben:

\n\n
Warrior w;  // Alles gut, aber nicht polymorph
\n\n

Was aber, wenn der Charaktertyp erst während des Spiels gewählt wird? Nur ein Zeiger bietet die nötige Flexibilität:

\n\n
Character* hero;\nif (choice == "warrior")\n    hero = new Warrior();\nelse\n    hero = new Mage();\n\nhero->attack(); // Die Methode der Ableitung wird aufgerufen!\ndelete hero;
\n\n

Unter der Haube: Der Compiler verwendet eine Tabelle virtueller Funktionen (vtable). Der Zeiger speichert die Adresse, unter der das Objekt liegt, und schaut beim Methodenaufruf in die vtable. Mit einem Objekt auf dem Stack funktioniert dieser Trick nicht – der Typ ist festgelegt.

\n\n

2. Übergabe an eine Funktion ohne Kopie

\n

Schauen Sie sich diesen Code an:

\n\n
void process(Monster m) { // Übergabe per Wert!\n    m.roar();\n}\n\nMonster bigBoss;\nprocess(bigBoss); // Kopieren!
\n\n

Hier erhält process eine Kopie des Objekts. Wenn Monster ein Array mit 100 MB enthält – haben Sie gerade 100 MB grundlos kopiert. Ein Zeiger löst das Problem:

\n\n
void process(Monster* m)                

Blogs

Buchempfehlungen