Quando devo scrivere esplicitamente this->member
in un metodo di una classe?
research.att.com/~bs/
sia adesso stroustrup.com
. Nuovo collegamento: stroustrup.com/bs_faq2.html#this
Quando devo scrivere esplicitamente this->member
in un metodo di una classe?
research.att.com/~bs/
sia adesso stroustrup.com
. Nuovo collegamento: stroustrup.com/bs_faq2.html#this
Risposte:
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
}
};
i
potrebbe non esistere in A
. Potrei avere un esempio?
template<> struct A<float> { float x; };
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
Esistono diversi motivi per cui potrebbe essere necessario utilizzare il this
puntatore in modo esplicito.
Anche se di solito non mi piace particolarmente, ho visto altri usare questo-> semplicemente per ottenere aiuto da intellisense!
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;
}
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.
const
funzione 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));
)
Ci sono pochi casi in cui usare this
deve essere usato e ci sono altri in cui usare il this
puntatore è 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 this
da 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 mName
per i membri e name
per i locali.
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->".
this->
banale da evitare (basta dare alla variabile locale un nome diverso). Tutti gli usi davvero interessanti di this
non sono nemmeno menzionati da questa risposta.
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.
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 {}
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;
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.
È necessario utilizzare this
per disambiguare tra parametri / variabili locali e variabili membro.
class Foo
{
protected:
int myX;
public:
Foo(int myX)
{
this->myX = myX;
}
};
Lo scopo principale (o posso dire, l'unico) del this
puntatore è che punta all'oggetto utilizzato per invocare una funzione membro.
Sulla base di questo scopo, possiamo avere alcuni casi che solo l'uso del this
puntatore 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
}
};
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;
length
mutabile, o anche metterlo in una struttura annidata. Gettare via la costanza non è quasi mai una buona idea.
const
funzioni membro, dovrebbe essere mutable
. Altrimenti renderai la vita più complicata per te e per gli altri manutentori.