Menu

C++ Referenzen vs. Zeiger: Wann man was verwendet

Ein praktischer Vergleich von Referenzen und Zeigern in C++ - was sie gemeinsam haben, wo sie sich unterscheiden (Neubindung, Null, Arithmetik) und eine klare Regel, zu welchem man im Alltagscode greift.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Zwei Wege, auf etwas zu verweisen

Du kennst bereits Zeiger - Variablen, die eine Adresse speichern und dir den Zugriff auf das dort lebende Objekt ermöglichen. Referenzen sind das andere Werkzeug, das C++ dir gibt, um indirekt mit einem bestehenden Objekt zu arbeiten. Sie überschneiden sich genug, dass Anfänger oft nicht wissen, welches sie wählen sollen, also stellt diese Seite sie nebeneinander.

Die Kurzfassung: Eine Referenz ist ein Alias. Sobald int& r = x; ausgeführt wird, ist r gleich x - dasselbe Objekt, anderer Name. Ein Zeiger ist ein eigenständiges Objekt, das zufällig die Adresse eines anderen enthält. Dieser eine Unterschied bestimmt alles Weitere.

Eine Referenz ist ein Alias

Eine Referenz muss in dem Moment, in dem sie erzeugt wird, an ein Objekt gebunden werden, und von da an berührt jede Verwendung der Referenz das Original.

Beachte, dass es an der Verwendungsstelle kein * zum Dereferenzieren und kein & zum "Adresse nehmen" gibt - du liest und schreibst alias genau wie ein gewöhnliches int. Das & in int& alias ist Teil des Typs, nicht der Adressoperator.

Wo sie sich unterscheiden

Die folgenden Verhaltensweisen sind der eigentliche Grund, warum es beide Werkzeuge gibt. Das ist die Tabelle, die man sich merken sollte.

//                      reference            pointer
// must be initialized? yes                  no (but should be)
// can be null?         no                   yes (nullptr)
// can be reseated?     no                   yes
// pointer arithmetic?  no                   yes
// syntax to use it     just the name        *p  or  p->member
// taking address       &ref == &original    &p is the pointer's own address

Zwei davon bringen die Leute am meisten durcheinander. Erstens: Eine Referenz kann niemals neu gebunden werden - eine Zuweisung an sie kopiert einen Wert in das referenzierte Objekt, sie lässt die Referenz nicht auf etwas Neues zeigen.

Ein Zeiger hingegen kann jederzeit frei woanders hinzeigen:

Zweitens: Eine Referenz kann niemals legal null sein, ein Zeiger schon. Das macht "kein Wert" mit einem Zeiger darstellbar, mit einer Referenz aber nicht - eine Eigenschaft, auf die du dich ständig stützen wirst.

Die Wahl bei Funktionsparametern

Hier zeigt sich die Wahl am häufigsten. Wenn eine Funktion das Objekt des Aufrufers lesen oder ändern muss, funktionieren beide, aber sie signalisieren eine unterschiedliche Absicht.

Die Referenz-Variante (addTax(cart)) lässt sich unmöglich mit "nichts" aufrufen, daher prüfst du innerhalb der Funktion nie auf null - das Objekt ist garantiert vorhanden. Die Zeiger-Variante (applyDiscount(&cart)) macht an der Aufrufstelle über das & deutlich, dass das Argument geändert werden könnte, und sie erlaubt dem Aufrufer, nullptr zu übergeben, um "nicht zutreffend" zu bedeuten. Wähle diejenige, deren Garantie zu deiner Funktion passt.

Für schreibgeschützte Parameter großer Typen ist die idiomatische Wahl const T& - es vermeidet eine Kopie und verspricht, nicht zu ändern. Siehe Funktionsparameter für mehr zur Übergabe per Wert vs. per Referenz.

Eine einfache Faustregel

Wenn du unsicher bist, wähle standardmäßig eine Referenz und steige nur dann auf einen Zeiger um, wenn du eine Fähigkeit brauchst, die einer Referenz fehlt:

  • Verwende eine Referenz, wenn das Objekt immer existiert und seine Identität nie ändert - der häufige Fall bei Funktionsparametern und Aliassen.
  • Verwende einen Zeiger, wenn einer dieser Punkte zutrifft:
    • "Nichts" ist ein gültiger Zustand (optionales Argument, eine Suche, die keine Übereinstimmung findet) - ein Zeiger kann nullptr sein.
    • Du musst im Lauf der Zeit auf verschiedene Objekte zeigen - ein Zeiger kann neu gebunden werden.
    • Du verwaltest Heap-Speicher, den du mit delete freigibst, oder durchläufst ein Array mit Zeigerarithmetik.

Trifft nichts davon zu, ist eine Referenz die sauberere, sicherere Wahl, weil der Compiler "immer gültig, nie neu gebunden" für dich durchsetzt.

Häufige Fehler, die man vermeiden sollte

  • Erwarten, dass ref = other neu bindet. Stattdessen weist es einen Wert in das referenzierte Objekt hinein zu. Referenzen sind auf Lebenszeit gebunden; wenn du Neubindung brauchst, verwende einen Zeiger.
  • Eine Referenz (oder einen Zeiger) auf eine lokale Variable zurückgeben. int& f() { int x = 5; return x; } gibt eine hängende Referenz zurück - x stirbt, wenn f zurückkehrt, und das Ergebnis zu verwenden ist undefiniertes Verhalten. Dieselbe Falle trifft Zeiger (return &x;).
  • Eine "Null-Referenz" fälschen. int& r = *p; zu schreiben, wenn p nullptr ist, ist undefiniertes Verhalten in dem Moment, in dem du dereferenzierst, keine sichere "leere" Referenz. Drücke Optionalität stattdessen mit einem Zeiger oder std::optional aus.
  • Aus Gewohnheit zu einem Zeiger greifen. Wenn das Argument immer existiert und du nie neu binden wirst, beseitigt eine Referenz eine ganze Klasse von Null-Prüfungen und Abstürzen. Bezahle nicht für Fähigkeiten, die du nicht nutzt.

Weiter: Dynamischer Speicher

Bisher wurde jedes Objekt, das du referenziert oder auf das du gezeigt hast, automatisch auf dem Stack erzeugt. Die nächste Seite, dynamischer Speicher, behandelt new und delete - das Betriebssystem zur Laufzeit um Speicher bitten, warum Zeiger (nicht Referenzen) ihn besitzen und wie das Vergessen der Freigabe zu Lecks führt.

Häufig gestellte Fragen

Was ist der Unterschied zwischen einer Referenz und einem Zeiger in C++?

Eine Referenz ist ein Alias für ein bestehendes Objekt: Sie muss initialisiert werden, kann niemals null sein und kann danach niemals dazu gebracht werden, auf ein anderes Objekt zu verweisen. Ein Zeiger ist eine eigenständige Variable, die eine Adresse enthält: Er kann null sein, kann neu gebunden werden, um woanders hinzuzeigen, und unterstützt Zeigerarithmetik. Verwende die &-Syntax bei Referenzen und */-> bei Zeigern.

Wann sollte ich in C++ einen Zeiger statt einer Referenz verwenden?

Verwende einen Zeiger, wenn "nichts" ein gültiger Zustand ist (ein optionales Argument, ein nicht gefundenes Ergebnis), wenn du ihn neu binden musst, um im Lauf der Zeit auf verschiedene Objekte zu zeigen, oder wenn dir Heap-Speicher gehört, den du mit delete freigibst. Verwende eine Referenz, wenn das Objekt immer existiert und seine Identität nie ändert - das deckt die meisten Funktionsparameter ab.

Kann eine Referenz in C++ null sein?

Nein. Eine gültige Referenz verweist immer auf ein echtes Objekt, daher prüfst du sie nie auf null. Wenn du eine Referenz aus einem dereferenzierten Nullzeiger erstellst (int& r = *p;, wobei p null ist), erhältst du undefiniertes Verhalten, keine Null-Referenz. Wenn du "vielleicht nichts" ausdrücken musst, verwende einen Zeiger oder std::optional.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S