Ricerca…


Osservazioni

this puntatore è una parola chiave per C ++ quindi non è necessaria alcuna libreria per implementarlo. E non dimenticare che this è un puntatore! Quindi non puoi fare:

 this.someMember();

Mentre accedi alle funzioni membro o alle variabili membro dai puntatori usando il simbolo freccia -> :

this->someMember();

Altri link utili per una migliore comprensione di this puntatore:

Qual è il puntatore "questo"?

http://www.geeksforgeeks.org/this-pointer-in-c/

https://www.tutorialspoint.com/cplusplus/cpp_this_pointer.htm

questo puntatore

Tutte le funzioni membro non statiche hanno un parametro nascosto, un puntatore a un'istanza della classe, chiamato this ; questo parametro viene inserito silenziosamente all'inizio dell'elenco dei parametri e gestito interamente dal compilatore. Quando un membro della classe si accede all'interno di una funzione membro, viene silenziosamente accede attraverso this ; ciò consente al compilatore di utilizzare un'unica funzione membro non statica per tutte le istanze e consente a una funzione membro di chiamare polimorficamente altre funzioni membro.

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 un costruttore, this può essere tranquillamente usato per accedere (implicitamente o esplicitamente) a qualsiasi campo che è già stato inizializzato, o qualsiasi campo in una classe genitore; viceversa, (implicitamente o esplicitamente) l'accesso a tutti i campi che non sono ancora stati inizializzati, o qualsiasi campo in una classe derivata, non è sicuro (a causa della classe derivata non ancora costruita, e quindi i suoi campi non vengono né inizializzati né esistenti). È anche pericoloso chiamare le funzioni dei membri virtuali attraverso this nel costruttore, poiché qualsiasi funzione di classe derivata non sarà considerata (a causa della classe derivata che non è ancora stata costruita, e quindi il suo costruttore non ha ancora aggiornato il vtable).


Si noti inoltre che mentre in un costruttore, il tipo dell'oggetto è il tipo che il costruttore costruisce. Ciò vale anche se l'oggetto è dichiarato come un tipo derivato. Ad esempio, nell'esempio seguente, ctd_good e ctd_bad sono di tipo CtorThisBase all'interno di CtorThisBase() e digitano CtorThis all'interno di CtorThis() , anche se il loro tipo canonico è CtorThisDerived . Poiché le classi più derivate sono costruite attorno alla classe base, l'istanza passa gradualmente attraverso la gerarchia delle classi fino a quando non è un'istanza completamente costruita del tipo desiderato.

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);

Con queste classi e funzioni membro:

  • Nel buon costruttore, per ctd_good :
    • CtorThisBase è completamente costruito dal momento in CtorThis viene inserito il costruttore CtorThis . Pertanto, s è in uno stato valido durante l'inizializzazione di i , e può quindi essere accessibile.
    • i viene inizializzato prima che j(this->i) venga raggiunto. Pertanto, i è in uno stato valido durante l'inizializzazione di j e può quindi essere accessibile.
    • j viene inizializzato prima che k(j) venga raggiunto. Pertanto, j è in uno stato valido durante l'inizializzazione di k e può quindi essere accessibile.
  • Nel costruttore ctd_bad , per ctd_bad :
    • k viene inizializzato dopo che j(this->k) viene raggiunto. Pertanto, k è in uno stato non valido durante l'inizializzazione di j , e accedervi causa un comportamento indefinito.
    • CtorThisDerived non viene CtorThisDerived fino a dopo la CtorThis . Pertanto, b è in uno stato non valido durante l'inizializzazione di k , e accedervi causa un comportamento indefinito.
    • L'oggetto ctd_bad è ancora un CtorThis fino a quando non lascia CtorThis() , e non sarà aggiornato per utilizzare CtorThisDerived vtable 's fino CtorThisDerived() . Pertanto, virt_func() chiamerà CtorThis::virt_func() , indipendentemente dal fatto che sia destinato a chiamarlo o CtorThisDerived::virt_func() .

Usando questo puntatore per accedere ai dati dei membri

In questo contesto, l'utilizzo di this puntatore non è del tutto necessario, ma renderà il codice più chiaro al lettore, indicando che una determinata funzione o variabile è un membro della classe. Un esempio in questa situazione:

// 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;
}

Guardalo in azione qui .

Usando questo puntatore per distinguere tra dati e parametri dei membri

Questa è una strategia utile per differenziare i dati dei membri dai parametri ... Prendiamo questo esempio:

// 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();
}

Puoi vedere qui nel costruttore che eseguiamo quanto segue:

this->name = name; 

Qui, puoi vedere che stiamo assingendo il nome del parametro al nome della variabile privata dalla classe Dog (this-> name).

Per vedere l'output del codice precedente: http://cpp.sh/75r7

questo Pointer CV-qualificatori

this può anche essere qualificato cv, lo stesso di qualsiasi altro puntatore. Tuttavia, poiché this parametro non è elencato nell'elenco dei parametri, è necessaria una sintassi speciale; i qualificatori di cv sono elencati dopo la lista dei parametri, ma prima del corpo della funzione.

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*
};

Poiché this tratta di un parametro, è possibile sovraccaricare una funzione in base a this cv-qualificatore / i .

struct CVOverload {
    int func()                { return    3; }
    int func() const          { return   33; }
    int func() volatile       { return  333; }
    int func() const volatile { return 3333; }
};

Quando this è const (includendo const volatile ), la funzione non è in grado di scrivere attraverso le variabili membro, sia implicitamente che esplicitamente. L'unica eccezione è rappresentata dalle variabili dei membri mutable , che possono essere scritte indipendentemente dalla costanza. A causa di ciò, const viene utilizzato per indicare che la funzione membro non modifica lo stato logico dell'oggetto (il modo in cui l'oggetto appare al mondo esterno), anche se modifica lo stato fisico (il modo in cui l'oggetto appare sotto la cappa ).

Lo stato logico è il modo in cui l'oggetto appare agli osservatori esterni. Non è direttamente legato allo stato fisico, anzi, potrebbe non essere nemmeno memorizzato come stato fisico. Finché gli osservatori esterni non possono vedere alcuna modifica, lo stato logico è costante, anche se si capovolge ogni singolo bit nell'oggetto.

Lo stato fisico, noto anche come stato bit per bit, è il modo in cui l'oggetto viene archiviato in memoria. Questo è il nitty-gritty dell'oggetto, i raw 1 e 0 che compongono i suoi dati. Un oggetto è fisicamente costante solo se la sua rappresentazione in memoria non cambia mai.

Si noti che le basi C ++ const ness sullo stato logico, non lo stato fisico.

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;
}

Nota che mentre tecnicamente potresti usare const_cast su this per renderlo non-cv-qualificato, in realtà, DAVVERO , non dovresti, e dovresti usare invece il mutable . Un const_cast è suscettibile di invocare un comportamento indefinito quando viene utilizzato su un oggetto che è effettivamente const , mentre mutable è progettato per essere sicuro da usare. Tuttavia, è possibile che si possa imbattersi in questo codice estremamente vecchio.

Un'eccezione a questa regola è la definizione di accessori non cv-qualificati in termini di accessors const ; poiché l'oggetto è garantito non essere const se viene chiamata la versione non cv-qualificata, non c'è alcun rischio di UB.

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));
    }
};

Questo previene inutili duplicazioni di codice.


Come con i puntatori regolari, se this è volatile (incluso const volatile ), viene caricato dalla memoria ogni volta che si accede, invece di essere memorizzato nella cache. Questo ha gli stessi effetti sull'ottimizzazione come dichiarare qualsiasi altro puntatore volatile , quindi bisogna fare attenzione.


Si noti che se un'istanza è cv qualificato, le uniche funzioni membro è autorizzato ad accedere sono funzioni membro cui this puntatore è almeno altrettanto cv qualificato come istanza stessa:

  • Le istanze non cv possono accedere a qualsiasi funzione membro.
  • const istanze const possono accedere a funzioni const e const volatile .
  • volatile istanze volatile possono accedere a funzioni volatile e const volatile .
  • const volatile istanze const volatile possono accedere a funzioni const volatile .

Questo è uno dei principi chiave della correttezza const .

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.

questo Pointer Ref-Qualificatori

C ++ 11

Analogamente a this CV-qualificazioni, possiamo applicare anche ref-qualificazioni a *this . I qualificatori Ref vengono utilizzati per scegliere tra semantica di riferimento normale e valore di riferimento, consentendo al compilatore di utilizzare la copia o spostare la semantica a seconda di quali sono più appropriati e vengono applicati a *this invece di this .

Si noti che, nonostante i qualificatori di riferimento che usano la sintassi di riferimento, this stesso è ancora un puntatore. Si noti inoltre che i qualificatori di ref non cambiano effettivamente il tipo di *this ; è solo più facile descrivere e capire i loro effetti guardandoli come se lo avessero fatto.

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

Una funzione membro non può avere sovraccarichi sia con che senza qualificatori di ref; il programmatore deve scegliere tra l'uno o l'altro. Fortunatamente, i qualificatori di cv possono essere usati insieme ai qualificatori di ref, permettendo di seguire le regole di correttezza delle const .

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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow