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:

Was ist der "Dies" -Zeiger?

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 :
    • CtorThisBase ist vollständig erstellt, wenn der CtorThis Konstruktor eingegeben wird. Daher befindet sich s während der Initialisierung von i in einem gültigen Zustand und kann somit aufgerufen werden.
    • i wird initialisiert, bevor j(this->i) erreicht ist. Daher befindet sich i während der Initialisierung von j in einem gültigen Zustand und kann somit darauf zugegriffen werden.
    • j wird initialisiert, bevor k(j) erreicht wird. Daher befindet sich j während der Initialisierung von k in einem gültigen Zustand und kann somit darauf zugegriffen werden.
  • Im schlechten Konstruktor für ctd_bad :
    • k wird initialisiert, nachdem j(this->k) erreicht ist. Daher ist k während der Initialisierung von j in einem ungültigen Zustand, und der Zugriff darauf verursacht ein undefiniertes Verhalten.
    • CtorThisDerived wird erst nach CtorThis . Daher ist b während der Initialisierung von k in einem ungültigen Zustand, und der Zugriff darauf verursacht ein undefiniertes Verhalten.
    • Das Objekt ctd_bad ist noch ein CtorThis bis es CtorThis() verlässt, und wird nicht aktualisiert, um die CtorThisDerived CtorThisDerived bis CtorThisDerived() . Daher virt_func() CtorThis::virt_func() , unabhängig davon, ob dies oder CtorThisDerived::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.

Eine Ausnahme von dieser Regel ist das Definieren nicht-qualifizierter Zugriffsmethoden in Bezug auf const Zugriffsmethoden. Da das Objekt garantiert nicht const wenn die nicht für den CV geeignete Version aufgerufen wird, besteht kein UB-Risiko.

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.
  • const Instanzen können auf const volatile Funktionen von const und const volatile zugreifen.
  • volatile Instanzen können auf volatile und const volatile Funktionen zugreifen.
  • const volatile Instanzen können auf const volatile Funktionen 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

C ++ 11

Ä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&& {}
};


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow