Quando dovrei fare un uso esplicito del puntatore `this`?


97

Quando devo scrivere esplicitamente this->memberin un metodo di una classe?


16
Sono sicuro che questo sia uno stupido, ma ovviamente non è ricercabile. Non per la prima volta, vorrei che questo puntatore si chiamasse auto!

5
Non solo, vorrei che fosse un riferimento.
rlbond

2
Stesso. : | Ecco perché, a proposito: research.att.com/~bs/bs_faq2.html#this
GManNickG

11
Questo metodo ovviamente non funziona se la persona non conosce la risposta.
CHIEDERE

3
@ JohnH .: Hm, sembra che research.att.com/~bs/sia adesso stroustrup.com. Nuovo collegamento: stroustrup.com/bs_faq2.html#this
GManNickG

Risposte:


118

Di solito, non è necessario, this->è implicito.

A volte, c'è un'ambiguità del nome, dove può essere usata per disambiguare i membri della classe e le variabili locali. Tuttavia, ecco un caso completamente diverso in cui this->è esplicitamente richiesto.

Considera il codice seguente:

template<class T>
struct A {
   int i;
};

template<class T>
struct B : A<T> {

    int foo() {
        return this->i;
    }

};

int main() {
    B<int> b;
    b.foo();
}

Se ometti this->, il compilatore non sa come trattare i, poiché può o non può esistere in tutte le istanze di A. Per dirgli che iè effettivamente un membro di A<T>, per qualsiasi T, this->è richiesto il prefisso.

Nota: è ancora possibile omettere il this->prefisso utilizzando:

template<class T>
struct B : A<T> {

    using A<T>::i; // explicitly refer to a variable in the base class

    int foo() {
        return i; // i is now known to exist
    }

};

8
Buon uso della dichiarazione d'uso :)
Faisal Vali,

3
Questo è un caso particolarmente brutto. Ne sono stato morso prima.
Jason Baker

5
Questa potrebbe essere una domanda sciocca, ma non capisco perché ipotrebbe non esistere in A. Potrei avere un esempio?
Cam Jackson

1
@CamJackson Ho provato il codice su Visual Studio. i risultati sono gli stessi indipendentemente dal fatto che "questo->" sia esistito o meno. Qualche idea?
Peng Zhang

8
@CamJackson: si possono specializzare le classi sul tipo:template<> struct A<float> { float x; };
Macke

31

Se dichiari una variabile locale in un metodo con lo stesso nome di un membro esistente, dovrai usare this-> var per accedere al membro della classe invece della variabile locale.

#include <iostream>
using namespace std;
class A
{
    public:
        int a;

        void f() {
            a = 4;
            int a = 5;
            cout << a << endl;
            cout << this->a << endl;
        }
};

int main()
{
    A a;
    a.f();
}

stampe:

5
4


1
È meglio usare cout << A :: a << endl; anziché. "questo" non è importante in questo caso.
siddhant3s

3
Preferisco semplicemente evitare che il nome si scontri con convenzioni come "m_a" o "a_".
Tom

19

Esistono diversi motivi per cui potrebbe essere necessario utilizzare il thispuntatore in modo esplicito.

  • Quando vuoi passare un riferimento al tuo oggetto a qualche funzione.
  • Quando è presente un oggetto dichiarato localmente con lo stesso nome dell'oggetto membro.
  • Quando stai tentando di accedere ai membri delle classi di base dipendenti .
  • Alcune persone preferiscono la notazione per disambiguare visivamente gli accessi dei membri nel loro codice.

7

Anche se di solito non mi piace particolarmente, ho visto altri usare questo-> semplicemente per ottenere aiuto da intellisense!


6
  1. Dove una variabile membro sarebbe nascosta da una variabile locale
  2. Se vuoi solo rendere esplicitamente chiaro che stai chiamando un metodo / variabile di istanza


Alcuni standard di codifica utilizzano l'approccio (2) poiché affermano che rende il codice più facile da leggere.

Esempio:
supponiamo che MyClass abbia una variabile membro chiamata "count"

void MyClass::DoSomeStuff(void)
{
   int count = 0;

   .....
   count++;
   this->count = count;
}

5

Un altro caso è quando si invocano operatori. Ad esempio invece di

bool Type::operator!=(const Type& rhs)
{
    return !operator==(rhs);
}

si può dire

bool Type::operator!=(const Type& rhs)
{
    return !(*this == rhs);
}

Che potrebbe essere più leggibile. Un altro esempio è il copy-and-swap:

Type& Type::operator=(const Type& rhs)
{
    Type temp(rhs);
    temp.swap(*this);
}

Non so perché non sia scritto swap(temp)ma questo sembra essere comune.


Nel tuo ultimo caso, nota che puoi chiamare una constfunzione non membro su un temporaneo ( Type(rhs).swap(*this);è legale e corretto) ma un temporaneo non può legarsi a un parametro di riferimento non-const (il compilatore rifiuta swap(Type(rhs));così come this->swap(Type(rhs));)
Ben Voigt

5

Ci sono pochi casi in cui usare this deve essere usato e ci sono altri in cui usare il thispuntatore è un modo per risolvere un problema.

1) Alternative disponibili : per risolvere l'ambiguità tra variabili locali e membri della classe, come illustrato da @ASk .

2) Nessuna alternativa: per restituire un puntatore o un riferimento a thisda una funzione membro. Questo è spesso fatto (e dovrebbe essere fatto) quando il sovraccarico operator+, operator-, operator=, ecc:

class Foo
{
  Foo& operator=(const Foo& rhs)
  {
    return * this;
  }
};

In questo modo è possibile utilizzare un linguaggio noto come " concatenamento di metodi ", in cui si eseguono diverse operazioni su un oggetto in una riga di codice. Ad esempio:

Student st;
st.SetAge (21).SetGender (male).SetClass ("C++ 101");

Alcuni considerano questo consesso, altri lo considerano un abominio. Contami in quest'ultimo gruppo.

3) Nessuna alternativa: per risolvere i nomi in tipi dipendenti. Ciò si verifica quando si utilizzano modelli, come in questo esempio:

#include <iostream>


template <typename Val>
class ValHolder
{
private:
  Val mVal;
public:
  ValHolder (const Val& val)
  :
    mVal (val)
  {
  }
  Val& GetVal() { return mVal; }
};

template <typename Val>
class ValProcessor
:
  public ValHolder <Val>
{
public:
  ValProcessor (const Val& val)
  :
    ValHolder <Val> (val)
  {
  }

  Val ComputeValue()
  {
//    int ret = 2 * GetVal();  // ERROR:  No member 'GetVal'
    int ret = 4 * this->GetVal();  // OK -- this tells compiler to examine dependant type (ValHolder)
    return ret;
  }
};

int main()
{
  ValProcessor <int> proc (42);
  const int val = proc.ComputeValue();
  std::cout << val << "\n";
}

4) Alternative disponibili: come parte dello stile di codifica, per documentare quali variabili sono variabili membro rispetto alle variabili locali. Preferisco uno schema di denominazione diverso in cui i membri varibales non possono mai avere lo stesso nome dei locali. Attualmente sto usando mNameper i membri e nameper i locali.


4

Devi solo usare questo-> se hai un simbolo con lo stesso nome in due potenziali spazi dei nomi. Prendiamo ad esempio:

class A {
public:
   void setMyVar(int);
   void doStuff();

private:
   int myVar;
}

void A::setMyVar(int myVar)
{
  this->myVar = myVar;  // <- Interesting point in the code
}

void A::doStuff()
{
  int myVar = ::calculateSomething();
  this->myVar = myVar; // <- Interesting point in the code
}

Nei punti interessanti del codice, facendo riferimento a myVar si farà riferimento alla myVar locale (parametro o variabile). Per accedere al membro della classe chiamato anche myVar, è necessario utilizzare esplicitamente "this->".


Questo è l'unico uso this->banale da evitare (basta dare alla variabile locale un nome diverso). Tutti gli usi davvero interessanti di thisnon sono nemmeno menzionati da questa risposta.
cmaster - ripristina monica il

4

Gli altri usi per questo (come pensavo quando ho letto il riepilogo e metà della domanda ....), Ignorando la disambiguazione dei nomi (cattiva) in altre risposte, sono se si desidera eseguire il cast dell'oggetto corrente, collegarlo in un oggetto funzione o usalo con un puntatore a membro.

Cast

void Foo::bar() {
    misc_nonconst_stuff();
    const Foo* const_this = this;
    const_this->bar(); // calls const version

    dynamic_cast<Bar*>(this)->bar(); // calls specific virtual function in case of multi-inheritance
} 

void Foo::bar() const {}

Rilegatura

void Foo::baz() {
     for_each(m_stuff.begin(), m_stuff.end(),  bind(&Foo:framboozle, this, _1));        
     for_each(m_stuff.begin(), m_stuff.end(), [this](StuffUnit& s) { framboozle(s); });         
} 

void Foo::framboozle(StuffUnit& su) {}

std::vector<StuffUnit> m_stuff;

ptr a membro

void Foo::boz() {
    bez(&Foo::bar);
    bez(&Foo::baz);
} 

void Foo::bez(void (Foo::*func_ptr)()) {
    for (int i=0; i<3; ++i) {
        (this->*func_ptr)();
    }
}

Spero che aiuti a mostrare altri usi di questo oltre a questo-> membro.


3

È necessario utilizzare thisper disambiguare tra parametri / variabili locali e variabili membro.

class Foo
{
protected:
  int myX;

public:
  Foo(int myX)
  {
    this->myX = myX; 
  }
};

2
No, non ne hai bisogno , puoi usarlo . È inoltre possibile utilizzare un nome diverso per l'argomento della funzione, che ha il vantaggio di non avere due entità con lo stesso nome.
cmaster - ripristina monica il

3

Lo scopo principale (o posso dire, l'unico) del thispuntatore è che punta all'oggetto utilizzato per invocare una funzione membro.

Sulla base di questo scopo, possiamo avere alcuni casi che solo l'uso del thispuntatore può risolvere il problema.

Ad esempio, dobbiamo restituire l'oggetto invocante in una funzione membro con argomento è lo stesso oggetto di classe:

class human {

... 

human & human::compare(human & h){
    if (condition)
        return h;       // argument object
    else 
        return *this;   // invoking object
    }
};

2

Ho trovato un altro caso interessante di utilizzo esplicito del puntatore "this" nel libro Effective C ++.

Ad esempio, supponi di avere una funzione const come

  unsigned String::length() const

Non vuoi calcolare la lunghezza di String per ogni chiamata, quindi vuoi memorizzarla nella cache facendo qualcosa di simile

  unsigned String::length() const
  {
    if(!lengthInitialized)
    {
      length = strlen(data);
      lengthInitialized = 1;
    }
  }

Ma questo non verrà compilato: stai cambiando l'oggetto in una funzione const.

Il trucco per risolvere questo problema richiede il cast di questo a un non-const this :

  String* const nonConstThis = (String* const) this;

Quindi, sarai in grado di fare in alto

  nonConstThis->lengthInitialized = 1;

3
Oppure potresti renderlo lengthmutabile, o anche metterlo in una struttura annidata. Gettare via la costanza non è quasi mai una buona idea.
Richard J. Ross III

3
Per favore non farlo. Se il membro deve essere modificato dalle constfunzioni membro, dovrebbe essere mutable. Altrimenti renderai la vita più complicata per te e per gli altri manutentori.
David Rodríguez - dribeas
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.