C++
Der This Pointer
Suche…
Bemerkungen
this Zeiger ist ein Schlüsselwort für C ++, daher ist keine Bibliothek erforderlich, um dies zu implementieren. Und vergiss nicht, this ist ein Zeiger! Du kannst also nicht:
this.someMember();
Wenn Sie mit dem Pfeilsymbol auf Elementfunktionen oder Elementvariablen zugreifen, verwenden Sie das Pfeilsymbol -> :
this->someMember();
Weitere hilfreiche Links zum besseren Verständnis this Zeigers:
http://www.geeksforgeeks.org/this-pointer-in-c/
https://www.tutorialspoint.com/cplusplus/cpp_this_pointer.htm
dieser Zeiger
Alle nicht statischen Memberfunktionen haben einen ausgeblendeten Parameter, einen Zeiger auf eine Instanz der Klasse mit dem Namen this . Dieser Parameter wird am Anfang der Parameterliste automatisch eingefügt und vollständig vom Compiler behandelt. Wenn innerhalb einer Member-Funktion auf ein Member der Klasse zugegriffen wird, wird durch this Funktion im Hintergrund darauf zugegriffen. Dadurch kann der Compiler eine einzige nicht statische Memberfunktion für alle Instanzen verwenden, und eine Memberfunktion kann andere Memberfunktionen polymorph aufrufen.
struct ThisPointer {
int i;
ThisPointer(int ii);
virtual void func();
int get_i() const;
void set_i(int ii);
};
ThisPointer::ThisPointer(int ii) : i(ii) {}
// Compiler rewrites as:
ThisPointer::ThisPointer(int ii) : this->i(ii) {}
// Constructor is responsible for turning allocated memory into 'this'.
// As the constructor is responsible for creating the object, 'this' will not be "fully"
// valid until the instance is fully constructed.
/* virtual */ void ThisPointer::func() {
if (some_external_condition) {
set_i(182);
} else {
i = 218;
}
}
// Compiler rewrites as:
/* virtual */ void ThisPointer::func(ThisPointer* this) {
if (some_external_condition) {
this->set_i(182);
} else {
this->i = 218;
}
}
int ThisPointer::get_i() const { return i; }
// Compiler rewrites as:
int ThisPointer::get_i(const ThisPointer* this) { return this->i; }
void ThisPointer::set_i(int ii) { i = ii; }
// Compiler rewrites as:
void ThisPointer::set_i(ThisPointer* this, int ii) { this->i = ii; }
In einem Konstruktor kann this sicher verwendet werden, um (implizit oder explizit) auf ein Feld zuzugreifen, das bereits initialisiert wurde, oder auf ein Feld in einer übergeordneten Klasse. umgekehrt ist (implizit oder explizit) der Zugriff auf Felder, die noch nicht initialisiert wurden, oder auf Felder in einer abgeleiteten Klasse, unsicher (da die abgeleitete Klasse noch nicht erstellt wurde und ihre Felder daher weder initialisiert noch vorhanden sind). Es ist auch nicht sicher virtuelle Mitgliederfunktionen bis hin zu nennen this im Konstruktor, wie alle abgeleiteten Klasse Funktionen werden nicht berücksichtigt (aufgrund der abgeleiteten Klasse noch nicht aufgebaut ist, und somit der Konstruktor noch nicht die V - Tabelle zu aktualisieren).
Beachten Sie außerdem, dass der Typ des Objekts in einem Konstruktor der Typ ist, den dieser Konstruktor erstellt. Dies gilt auch dann, wenn das Objekt als abgeleiteter Typ deklariert ist. In dem folgenden Beispiel sind ctd_good und ctd_bad beispielsweise CtorThisBase in CtorThisBase() und CtorThis in CtorThis() , obwohl der kanonische Typ CtorThisDerived . Da die stärker abgeleiteten Klassen um die Basisklasse herum aufgebaut werden, durchläuft die Instanz nach und nach die Klassenhierarchie, bis es sich um eine vollständig konstruierte Instanz des beabsichtigten Typs handelt.
class CtorThisBase {
short s;
public:
CtorThisBase() : s(516) {}
};
class CtorThis : public CtorThisBase {
int i, j, k;
public:
// Good constructor.
CtorThis() : i(s + 42), j(this->i), k(j) {}
// Bad constructor.
CtorThis(int ii) : i(ii), j(this->k), k(b ? 51 : -51) {
virt_func();
}
virtual void virt_func() { i += 2; }
};
class CtorThisDerived : public CtorThis {
bool b;
public:
CtorThisDerived() : b(true) {}
CtorThisDerived(int ii) : CtorThis(ii), b(false) {}
void virt_func() override { k += (2 * i); }
};
// ...
CtorThisDerived ctd_good;
CtorThisDerived ctd_bad(3);
Mit diesen Klassen und Memberfunktionen:
- Im guten Konstruktor für
ctd_good:-
CtorThisBaseist vollständig erstellt, wenn derCtorThisKonstruktor eingegeben wird. Daher befindet sichswährend der Initialisierung voniin einem gültigen Zustand und kann somit aufgerufen werden. -
iwird initialisiert, bevorj(this->i)erreicht ist. Daher befindet sichiwährend der Initialisierung vonjin einem gültigen Zustand und kann somit darauf zugegriffen werden. -
jwird initialisiert, bevork(j)erreicht wird. Daher befindet sichjwährend der Initialisierung vonkin einem gültigen Zustand und kann somit darauf zugegriffen werden.
-
- Im schlechten Konstruktor für
ctd_bad:-
kwird initialisiert, nachdemj(this->k)erreicht ist. Daher istkwährend der Initialisierung vonjin einem ungültigen Zustand, und der Zugriff darauf verursacht ein undefiniertes Verhalten. -
CtorThisDerivedwird erst nachCtorThis. Daher istbwährend der Initialisierung vonkin einem ungültigen Zustand, und der Zugriff darauf verursacht ein undefiniertes Verhalten. - Das Objekt
ctd_badist noch einCtorThisbis esCtorThis()verlässt, und wird nicht aktualisiert, um dieCtorThisDerivedCtorThisDerived bisCtorThisDerived(). Dahervirt_func()CtorThis::virt_func(), unabhängig davon, ob dies oderCtorThisDerived::virt_func().
-
Verwenden dieses Zeigers für den Zugriff auf Mitgliedsdaten
In diesem Zusammenhang ist die Verwendung des Zeigers ' this nicht unbedingt erforderlich, macht den Code jedoch für den Leser klarer, indem er anzeigt, dass eine bestimmte Funktion oder Variable eine Klasse ist. Ein Beispiel in dieser Situation:
// Example for this pointer
#include <iostream>
#include <string>
using std::cout;
using std::endl;
class Class
{
public:
Class();
~Class();
int getPrivateNumber () const;
private:
int private_number = 42;
};
Class::Class(){}
Class::~Class(){}
int Class::getPrivateNumber() const
{
return this->private_number;
}
int main()
{
Class class_example;
cout << class_example.getPrivateNumber() << endl;
}
Sehen sie in Aktion hier .
Verwenden dieses Zeigers zur Unterscheidung zwischen Mitgliedsdaten und Parametern
Dies ist eine nützliche Strategie, um Elementdaten von Parametern zu unterscheiden. Nehmen wir folgendes Beispiel:
// Dog Class Example
#include <iostream>
#include <string>
using std::cout;
using std::endl;
/*
* @class Dog
* @member name
* Dog's name
* @function bark
* Dog Barks!
* @function getName
* To Get Private
* Name Variable
*/
class Dog
{
public:
Dog(std::string name);
~Dog();
void bark() const;
std::string getName() const;
private:
std::string name;
};
Dog::Dog(std::string name)
{
/*
* this->name is the
* name variable from
* the class dog . and
* name is from the
* parameter of the function
*/
this->name = name;
}
Dog::~Dog(){}
void Dog::bark() const
{
cout << "BARK" << endl;
}
std::string Dog::getName() const
{
return this->name;
}
int main()
{
Dog dog("Max");
cout << dog.getName() << endl;
dog.bark();
}
Sie können hier im Konstruktor sehen, dass wir Folgendes ausführen:
this->name = name;
Hier sehen Sie, dass wir den Parameternamen dem Namen der privaten Variablen der Klasse Dog (this-> name) zuordnen.
So sehen Sie die Ausgabe des obigen Codes: http://cpp.sh/75r7
diese Pointer CV-Qualifiers
this kann auch cv-qualifiziert sein, genauso wie jeder andere Zeiger. Da this Parameter jedoch nicht in der Parameterliste aufgeführt ist, ist hierfür eine spezielle Syntax erforderlich. Die CV-Qualifikationsmerkmale werden nach der Parameterliste aufgeführt, jedoch vor dem Hauptteil der Funktion.
struct ThisCVQ {
void no_qualifier() {} // "this" is: ThisCVQ*
void c_qualifier() const {} // "this" is: const ThisCVQ*
void v_qualifier() volatile {} // "this" is: volatile ThisCVQ*
void cv_qualifier() const volatile {} // "this" is: const volatile ThisCVQ*
};
Da this um einen Parameter handelt, kann eine Funktion auf der Grundlage this cv-Qualifier (s) überladen werden .
struct CVOverload {
int func() { return 3; }
int func() const { return 33; }
int func() volatile { return 333; }
int func() const volatile { return 3333; }
};
Wenn this const (einschließlich const volatile ), kann die Funktion weder implizit noch explizit in Member-Variablen schreiben. Die einzige Ausnahme hiervon sind mutable Membervariablen , die unabhängig von der Konstante geschrieben werden können. Aus diesem Grund wird mit const angezeigt, dass die Member-Funktion den logischen Zustand des Objekts (die Art, wie das Objekt der Außenwelt erscheint) nicht ändert, selbst wenn es den physischen Zustand (die Art, wie das Objekt unter der Haube aussieht) ändert ).
Der logische Zustand ist die Art und Weise, wie das Objekt außerhalb der Beobachter erscheint. Es ist nicht direkt an den physischen Zustand gebunden und wird möglicherweise nicht einmal als physischer Zustand gespeichert. Solange externe Beobachter keine Änderungen sehen können, ist der logische Zustand konstant, selbst wenn Sie jedes einzelne Bit im Objekt umdrehen.
Der physikalische Zustand, auch als bitweiser Zustand bezeichnet, ist, wie das Objekt im Speicher abgelegt wird. Dies ist das Kleinste des Objekts, die rohen Einsen und Nullen, aus denen seine Daten bestehen. Ein Objekt ist nur physisch konstant, wenn sich seine Darstellung im Speicher niemals ändert.
Beachten Sie, dass C ++ Basen const auf logischen Zustand ness, nicht physischen Zustand.
class DoSomethingComplexAndOrExpensive {
mutable ResultType cached_result;
mutable bool state_changed;
ResultType calculate_result();
void modify_somehow(const Param& p);
// ...
public:
DoSomethingComplexAndOrExpensive(Param p) : state_changed(true) {
modify_somehow(p);
}
void change_state(Param p) {
modify_somehow(p);
state_changed = true;
}
// Return some complex and/or expensive-to-calculate result.
// As this has no reason to modify logical state, it is marked as "const".
ResultType get_result() const;
};
ResultType DoSomethingComplexAndOrExpensive::get_result() const {
// cached_result and state_changed can be modified, even with a const "this" pointer.
// Even though the function doesn't modify logical state, it does modify physical state
// by caching the result, so it doesn't need to be recalculated every time the function
// is called. This is indicated by cached_result and state_changed being mutable.
if (state_changed) {
cached_result = calculate_result();
state_changed = false;
}
return cached_result;
}
Beachten Sie, dass Sie technisch nutzen könnten const_cast auf this , um es nicht-cv-qualifiziert zu machen, sollten Sie wirklich, wirklich nicht, und sollte verwenden mutable statt. Ein const_cast haftet nicht definiertes Verhalten aufzurufen , wenn auf ein Objekt verwendet , die tatsächlich ist const , während mutable ausgelegt ist , um sicher zu sein zu verwenden. Es ist jedoch möglich, dass Sie in sehr altem Code darauf stoßen.
class CVAccessor {
int arr[5];
public:
const int& get_arr_element(size_t i) const { return arr[i]; }
int& get_arr_element(size_t i) {
return const_cast<int&>(const_cast<const CVAccessor*>(this)->get_arr_element(i));
}
};
Dies verhindert unnötige Duplizierungen von Code.
Wenn this volatile (einschließlich const volatile ), wird es wie bei normalen Zeigern bei jedem Zugriff aus dem Speicher geladen, anstatt zwischengespeichert zu werden. Dies hat die gleichen Auswirkungen auf die Optimierung wie die Angabe eines anderen Zeigers als volatile , weshalb Vorsicht geboten ist.
Beachten Sie, dass , wenn eine Instanz cv-qualifiziert ist, ist es die einzige Mitglied Funktionen zugreifen , sind Funktionen , dessen Mitglieds ist erlaubt this Zeiger ist mindestens so cv-qualifiziert als Instanz selbst:
- Nicht-CV-Instanzen können auf alle Member-Funktionen zugreifen.
-
constInstanzen können aufconst volatileFunktionen vonconstundconst volatilezugreifen. -
volatileInstanzen können aufvolatileundconst volatileFunktionen zugreifen. -
const volatileInstanzen können aufconst volatileFunktionen zugreifen.
Dies ist einer der wichtigsten Grundsätze der const Korrektheit .
struct CVAccess {
void func() {}
void func_c() const {}
void func_v() volatile {}
void func_cv() const volatile {}
};
CVAccess cva;
cva.func(); // Good.
cva.func_c(); // Good.
cva.func_v(); // Good.
cva.func_cv(); // Good.
const CVAccess c_cva;
c_cva.func(); // Error.
c_cva.func_c(); // Good.
c_cva.func_v(); // Error.
c_cva.func_cv(); // Good.
volatile CVAccess v_cva;
v_cva.func(); // Error.
v_cva.func_c(); // Error.
v_cva.func_v(); // Good.
v_cva.func_cv(); // Good.
const volatile CVAccess cv_cva;
cv_cva.func(); // Error.
cv_cva.func_c(); // Error.
cv_cva.func_v(); // Error.
cv_cva.func_cv(); // Good.
diese Pointer Ref-Qualifiers
Ähnlich wie bei this CV-Qualifiers können wir auch Ref-Qualifier auf *this anwenden. Ref-Qualifizierer verwendet , um zwischen normal und rvalue Referenz Semantik zu wählen, so dass der Compiler entweder Kopie verwenden oder Semantik je nachdem , welche besser geeignet bewegen, und werden angewandt *this anstelle von this .
Beachten Sie, dass trotz Ref-Qualifiers, die die Referenzsyntax verwenden, this selbst immer noch ein Zeiger ist. Beachten Sie auch, dass ref-Qualifier den Typ von *this nicht wirklich ändern. Es ist nur einfacher, ihre Auswirkungen zu beschreiben und zu verstehen, indem man sie so betrachtet, als ob sie es taten.
struct RefQualifiers {
std::string s;
RefQualifiers(const std::string& ss = "The nameless one.") : s(ss) {}
// Normal version.
void func() & { std::cout << "Accessed on normal instance " << s << std::endl; }
// Rvalue version.
void func() && { std::cout << "Accessed on temporary instance " << s << std::endl; }
const std::string& still_a_pointer() & { return this->s; }
const std::string& still_a_pointer() && { this->s = "Bob"; return this->s; }
};
// ...
RefQualifiers rf("Fred");
rf.func(); // Output: Accessed on normal instance Fred
RefQualifiers{}.func(); // Output: Accessed on temporary instance The nameless one
Eine Member-Funktion kann weder mit als auch ohne Ref-Qualifier Überladungen haben. Der Programmierer muss zwischen dem einen oder dem anderen wählen. Zum Glück können cv-qualifiers in Verbindung mit ref-qualifiers verwendet werden, so dass die const Richtigkeitsregeln befolgt werden können.
struct RefCV {
void func() & {}
void func() && {}
void func() const& {}
void func() const&& {}
void func() volatile& {}
void func() volatile&& {}
void func() const volatile& {}
void func() const volatile&& {}
};