Zeiger und Referenzen
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 13 von 104.
Nachdem Sie nun Stack- und Heap-Speicher verstehen, lassen Sie uns die Werkzeuge erkunden, mit denen Sie direkt mit Speicheradressen arbeiten können: Pointer und Referenzen.
Ein Pointer speichert die Speicheradresse einer anderen Variablen. Man deklariert ihn mit * und erhält eine Adresse mit &.
int value = 42;
int* ptr = &value; // ptr hält die Adresse von value
std::cout << ptr; // Gibt die Speicheradresse aus
std::cout << *ptr; // Dereferenzierung: gibt 42 ausEine Referenz ist ein Alias für eine bestehende Variable. Einmal gebunden, bezieht sie sich immer auf diese Variable. Man deklariert sie mit &.
int value = 42;
int& ref = value; // ref ist ein Alias für value
ref = 100; // Ändert value auf 100
std::cout << value; // Gibt 100 ausHauptunterschiede:
| Pointer | Referenzen |
|---|---|
| Können null sein | Müssen initialisiert werden |
| Können neu zugewiesen werden | Können nicht neu gebunden werden |
Verwenden Sie *, um auf den Wert zuzugreifen | Direkter Zugriff auf den Wert |
| Können auf nichts zeigen | Immer gültig |
Beide sind essenziell, um Objekte effizient an Funktionen zu übergeben, ohne sie zu kopieren, und um mit auf dem Heap zugewiesenem Speicher zu arbeiten. Referenzen sind sicherer und einfacher, während Zeiger mehr Flexibilität bieten, wenn Sie diese neu zuweisen oder auf null prüfen müssen.
Aufgabe
EinfachLassen Sie uns ein einfaches System zur Wertmanipulation erstellen, das demonstriert, wie Pointer und Referenzen beim Ändern von Daten unterschiedlich funktionieren.
Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:
ValueOps.h: Definieren Sie drei Funktionen, die Ganzzahlwerte auf unterschiedliche Weise ändern:doubleByPointer(int* ptr)— nimmt einen Pointer entgegen und verdoppelt den Wert, auf den er zeigttripleByReference(int& ref)— nimmt eine Referenz entgegen und verdreifacht den WertswapValues(int* a, int* b)— nimmt zwei Pointer entgegen und vertauscht die Werte, auf die sie zeigen
main.cpp: Lesen Sie zwei Ganzzahlen aus der Eingabe ein und demonstrieren Sie dann sowohl Pointer als auch Referenzen:- Geben Sie die ursprünglichen Werte aus:
"Original: <first> <second>" - Verwenden Sie
doubleByPointerauf den ersten Wert und geben Sie dann aus:"After double: <first>" - Verwenden Sie
tripleByReferenceauf den zweiten Wert und geben Sie dann aus:"After triple: <second>" - Verwenden Sie
swapValues, um beide Werte zu vertauschen, und geben Sie dann aus:"After swap: <first> <second>"
- Geben Sie die ursprünglichen Werte aus:
Wenn Sie Funktionen mit Pointern aufrufen, denken Sie daran, den Adressoperator (&) zu verwenden, um die Adresse der Variablen zu übergeben. Referenzen funktionieren natürlicher — übergeben Sie die Variable einfach direkt.
Binden Sie Ihre Header-Datei in main.cpp mit #include "ValueOps.h" ein.
Spickzettel
Ein Pointer (Zeiger) speichert die Speicheradresse einer anderen Variable. Deklarieren Sie ihn mit * und rufen Sie eine Adresse mit & ab:
int value = 42;
int* ptr = &value; // ptr hält die Adresse von value
std::cout << ptr; // Gibt die Speicheradresse aus
std::cout << *ptr; // Dereferenzierung: gibt 42 ausEine Referenz ist ein Alias für eine bestehende Variable. Deklarieren Sie diese mit &:
int value = 42;
int& ref = value; // ref ist ein Alias für value
ref = 100; // Ändert value auf 100
std::cout << value; // Gibt 100 ausWichtige Unterschiede:
| Pointer | Referenzen |
|---|---|
| Können null sein | Müssen initialisiert werden |
| Können neu zugewiesen werden | Können nicht neu gebunden werden |
Verwenden * für den Wertzugriff | Direkter Wertzugriff |
| Können auf nichts zeigen | Immer gültig |
Beim Aufruf von Funktionen mit Pointern verwenden Sie den Adressoperator (&), um die Adresse der Variable zu übergeben. Referenzen funktionieren direkt — übergeben Sie einfach die Variable.
Probier es selbst
#include <iostream>
#include "ValueOps.h"
using namespace std;
int main() {
// Lies zwei Ganzzahlen von der Eingabe
int first, second;
cin >> first;
cin >> second;
// Gib die ursprünglichen Werte aus
cout << "Original: " << first << " " << second << endl;
// TODO: Verwende doubleByPointer für den ersten Wert (Adresse mit & übergeben)
// TODO: Nach Verdopplung ausgeben
// TODO: Verwende tripleByReference für den zweiten Wert (Variable direkt übergeben)
// TODO: Nach Verdreifachung ausgeben
// TODO: Verwende swapValues, um beide Werte zu vertauschen (Adressen mit & übergeben)
// TODO: Nach dem Tausch ausgeben
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container