Verwendung des this-Zeigers in C++
In diesem Tutorial werden wir zuerst das Konzept des this
-Zeigers verstehen.
Anschließend demonstrieren wir die Verwendung anhand von Beispielen. Zuletzt werden wir sehen, wie sich this
von *this
unterscheidet.
Verwendung des this
-Zeigers in C++
Der this
-Zeiger ist einfach ein implizit verfügbarer Zeiger, der in einem nicht statischen Funktionsumfang einer Klasse auf das aufrufende Objekt verweist oder darauf zeigt. Wenn Sie es per Definition nicht verstanden haben, schauen wir uns einige Grundlagen an, um das Konzept zu verstehen.
Wir wissen, dass alle nicht statischen Datenelemente eine separate Kopie für jedes Objekt haben. Der Member-Funktionscode wird jedoch von allen Objekten gemeinsam genutzt.
Alle Objekte derselben Klasse greifen auf dieselbe Funktionsdefinition aus dem Codesegment zu, wenn sie es aufrufen müssen. Der Compiler muss die Informationen des aufrufenden Objekts (das gerade eine Elementfunktion aufruft) kennen, um auf das richtige Datenelement zuzugreifen oder es zu aktualisieren, das für dieses bestimmte aufrufende Objekt spezifisch ist.
Wie erhält der Compiler also die aufrufenden Objektinformationen, wenn die Funktionsdefinitionen gleich sind?
Die einfache Antwort lautet, dass der Compiler während der Kompilierung des Programms automatisch einen versteckten impliziten Zeigerparameter in den Memberfunktionen hinzufügt. Dieser implizite Zeiger ist als this
-Zeiger bekannt.
Immer wenn eine Mitgliedsfunktion durch ein bestimmtes Klassenobjekt aufgerufen wird, wird das aufrufende Objekt automatisch als verstecktes Argument an den this
-Zeiger geliefert. Jetzt kann der Compiler diesen Zeiger verwenden, um auf das richtige Datenelement zuzugreifen oder es zu modifizieren, das für das aufrufende Objekt spezifisch ist.
this
-Zeiger ist bei den statischen Mitgliedern der Klasse nicht erforderlich, da diese Mitglieder (Funktionen und Datenmitglieder) von allen Objekten gemeinsam genutzt werden.Die Aktualisierung oder der Zugriff auf die statischen Mitglieder ist nicht objektspezifisch. Wir können sogar auf diese Mitglieder zugreifen, ohne ein Objekt der Klasse zu erstellen.
Daher verwendet der Compiler bei diesen Membern nicht den Operator this
.
Schauen wir uns das Codebeispiel an, um zu verstehen, warum wir sagen, dass der Compiler implizit den Zeiger this
verwendet, und wann es praktisch ist.
#include <iostream>
#include <string>
using namespace std;
class Person {
private:
string Name;
public:
Person(string Name) {
this->Name = Name; // this->Name is the private member for this object
}
void PrintName() { cout << this->Name << endl; }
void PrintName1() { cout << Name << endl; }
};
int main() {
Person P("Alexa");
P.PrintName();
P.PrintName1();
return 0;
}
Das obige Codebeispiel definiert eine Person
-Klasse mit zwei verschiedenen Elementfunktionen, um das private Datenelement Name
zu drucken. Die erste Anweisung in der Funktion main()
erzeugt ein Person
-Objekt und übergibt Alexa
als Argument an den Konstruktor.
Jetzt hilft this->Name
im Konstruktor dem Compiler, zwischen dem lokalen Parameter Name
und dem privaten Datenelement Name
zu unterscheiden.
Der nachfolgende Code von main()
ruft printName()
und printName1()
über P auf (d. h. P wird zum aufrufenden Objekt).
Ausgabe:
Alexa
Alexa
Die Ausgabe beider Funktionen ist die gleiche. Denn der Compiler stellt this->
implizit Name
in PrintName1()
voran.
Unterschied zwischen this
und *this
in C++
Bisher sind wir mit dem Konzept klar, dass this
ein Zeiger auf ein Objekt ist. Bei einem obj
vom Typ Person
ist this
vom Typ Person*
.
Beachten Sie auch, dass der this
-Zeiger immer ein rvalue
ist, der nicht geändert werden kann. *this
dereferenziert jedoch den this
-Zeiger.
Nachdem wir einen ausreichenden Hintergrund durchgegangen sind, schauen wir uns einen Beispielcode an, um den Unterschied zwischen this
und *this
zu verstehen.
#include <iostream>
using namespace std;
class Counter {
private:
int Count;
public:
Counter() { this->Count = 0; }
void IncreaseCount() { Count++; }
void PrintCount() { cout << this->Count << endl; }
Counter* GetCount_Pointer() { return this; }
Counter GetCount_Copy() { return *this; }
Counter& GetCount_Reference() { return *this; }
};
int main() {
// Section-A
cout << "Sectio-A" << endl;
Counter C1;
C1.IncreaseCount();
Counter* CounterPtr =
C1.GetCount_Pointer(); // CounterObj will be pointing to C1
CounterPtr->IncreaseCount();
C1.PrintCount();
// Section-B
cout << "Section-B" << endl;
Counter C2;
C2 = C1.GetCount_Copy();
C1.IncreaseCount();
C1.PrintCount();
C2.PrintCount();
// Section-C
cout << "Section-B" << endl;
Counter& CounterRef = C1.GetCount_Reference();
CounterRef.PrintCount();
return 0;
}
Dieses Code-Snippet erstellt eine Counter
-Klasse mit mehreren Methoden zu GetCount
-Methoden. Die erste Methode (d. h. GetCount_Pointer
) gibt den Wert des this
-Zeigers zurück, der einfach eine Adresse des aufrufenden Objekts ist.
Die Methode GetCount_Copy
gibt *this
zurück, während der Rückgabetyp ein Objekt vom Typ Counter
ist. Diese Funktion gibt also eine tiefe Kopie des aufrufenden Objekts zurück, was bedeutet, dass das Ändern der zurückgegebenen Kopie keine Auswirkungen auf die ursprüngliche Kopie hat.
Die letzte Methode GetCount_Reference
gibt ebenfalls *this
zurück, aber der Rückgabetyp dieser Methode ist ein Referenzobjekt (d. h. Counter&
). Anstatt eine neue tiefe Kopie zu erstellen, gibt der Compiler einen Alias oder Verweis auf das ursprüngliche Objekt zurück.
Alle Änderungen, die über den Alias vorgenommen werden, werden auch im ursprünglichen Objekt widergespiegelt.
Bevor wir den Haupttreibercode besprechen, schauen wir uns die Ausgabe des Programms an.
Ausgabe:
Sectio-A
2
Section-B
3
2
Section-B
3
Abschnitt-A der Methode main
deklariert zuerst das Objekt C1
vom Typ Counter
und erhöht dessen Count
durch IncreaseCount()
. Später initialisiert es einen CounterPtr
-Zeiger mit der von C1.GetCount_Pointer()
zurückgegebenen Adresse.
Nun zeigt der CounterPtr
auf C1
. Daher ändert der Aufruf der Inkrementfunktion durch CounterPtr
auch das C1
.
C2 = C1.GetCount_Copy()
in Abschnitt-B kopiert den gesamten aktuellen Inhalt von C1
tief in C2
, da C1.GetCount_Copy()
durch eine Kopie von C1
ersetzt wird. Daher wirkt sich eine Erhöhung der Anzahl für C1
nicht auf C2
aus.
Der Abschnitt-C deklariert eine Referenzvariable vom Typ Counter
und initialisiert sie mit dem, was von C1.GetCount_Reference()
zurückgegeben wird. Da C1.GetCount_Reference()
einen Alias von C1
zurückgibt, wird CounterRef
zu einem anderen Namen für C1
.
Die Anweisung Counter& CounterRef = C1.GetCount_Reference();
wird logisch äquivalent zu Counter& CounterRef = C1
.
Husnain is a professional Software Engineer and a researcher who loves to learn, build, write, and teach. Having worked various jobs in the IT industry, he especially enjoys finding ways to express complex ideas in simple ways through his content. In his free time, Husnain unwinds by thinking about tech fiction to solve problems around him.
LinkedIn