Quand dois-je écrire explicitement this->member
dans une méthode d'une classe?
research.att.com/~bs/
c'est maintenant stroustrup.com
. Nouveau lien: stroustrup.com/bs_faq2.html#this
Quand dois-je écrire explicitement this->member
dans une méthode d'une classe?
research.att.com/~bs/
c'est maintenant stroustrup.com
. Nouveau lien: stroustrup.com/bs_faq2.html#this
Réponses:
Habituellement, vous n'êtes pas obligé, this->
est implicite.
Parfois, il y a une ambiguïté de nom, où il peut être utilisé pour lever l'ambiguïté des membres de classe et des variables locales. Cependant, voici un cas complètement différent où this->
est explicitement requis.
Considérez le code suivant:
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();
}
Si vous omettez this->
, le compilateur ne sait pas comment traiter i
, car il peut exister ou non dans toutes les instanciations de A
. Pour lui dire qu'il i
est effectivement membre de A<T>
, pour tout T
, le this->
préfixe est requis.
Remarque: il est possible de toujours omettre le this->
préfixe en utilisant:
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
pourrait ne pas exister A
. Puis-je avoir un exemple?
template<> struct A<float> { float x; };
Si vous déclarez une variable locale dans une méthode avec le même nom qu'un membre existant, vous devrez utiliser this-> var pour accéder au membre de classe au lieu de la variable 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();
}
imprime:
5
4
Il existe plusieurs raisons pour lesquelles vous devrez peut-être utiliser this
explicitement le pointeur.
Bien que je n'aime généralement pas ça, j'ai vu d'autres utiliser ça-> simplement pour obtenir de l'aide d'intellisense!
Certaines normes de codage utilisent l'approche (2) car elles prétendent qu'elle facilite la lecture du code.
Exemple:
Supposons que MyClass a une variable membre appelée 'count'
void MyClass::DoSomeStuff(void)
{
int count = 0;
.....
count++;
this->count = count;
}
Un autre cas est lors de l'appel d'opérateurs. Par exemple au lieu de
bool Type::operator!=(const Type& rhs)
{
return !operator==(rhs);
}
tu peux dire
bool Type::operator!=(const Type& rhs)
{
return !(*this == rhs);
}
Ce qui pourrait être plus lisible. Un autre exemple est le copy-and-swap:
Type& Type::operator=(const Type& rhs)
{
Type temp(rhs);
temp.swap(*this);
}
Je ne sais pas pourquoi ce n'est pas écrit, swap(temp)
mais cela semble être courant.
const
fonction non membre sur un temporaire ( Type(rhs).swap(*this);
c'est légal et correct) mais un temporaire ne peut pas se lier à un paramètre de référence non-const (le compilateur rejette swap(Type(rhs));
aussi bien que this->swap(Type(rhs));
)
Il y a peu de cas où l'utilisation this
doit être utilisée, et il y en a d'autres où l'utilisation du this
pointeur est un moyen de résoudre un problème.
1) Alternatives disponibles : pour résoudre l'ambiguïté entre les variables locales et les membres de la classe, comme illustré par @ASk .
2) Aucune alternative: pour renvoyer un pointeur ou une référence à this
une fonction membre. Cela se fait souvent (et doit être fait) en cas de surcharge operator+
, operator-
, operator=
, etc:
class Foo
{
Foo& operator=(const Foo& rhs)
{
return * this;
}
};
Cela permet un idiome appelé « chaînage de méthodes », dans lequel vous effectuez plusieurs opérations sur un objet dans une ligne de code. Tel que:
Student st;
st.SetAge (21).SetGender (male).SetClass ("C++ 101");
Certains considèrent cela comme une consise, d'autres le considèrent comme une abomination. Comptez-moi dans ce dernier groupe.
3) Aucune alternative: pour résoudre les noms dans les types dépendants. Cela se produit lors de l'utilisation de modèles, comme dans cet exemple:
#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) Alternatives disponibles: dans le cadre du style de codage, pour documenter les variables qui sont des variables membres par opposition aux variables locales. Je préfère un schéma de dénomination différent où les varibales des membres ne peuvent jamais avoir le même nom que les locaux. Actuellement, j'utilise mName
pour les membres et name
pour les locaux.
Vous ne devez utiliser this-> que si vous avez un symbole avec le même nom dans deux espaces de noms potentiels. Prends pour exemple:
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
}
Aux points intéressants du code, faire référence à myVar fera référence au local (paramètre ou variable) myVar. Afin d'accéder au membre de classe également appelé myVar, vous devez utiliser explicitement "this->".
this->
qui est triviale à éviter (donnez simplement un nom différent à la variable locale). Toutes les utilisations vraiment intéressantes de this
ne sont même pas mentionnées par cette réponse.
Les autres utilisations de cela (comme je le pensais en lisant le résumé et la moitié de la question ....), Sans tenir compte de la (mauvaise) désambiguïsation de la dénomination dans les autres réponses, sont si vous souhaitez convertir l'objet actuel, liez-le dans un objet fonction ou utilisez-le avec un pointeur vers un membre.
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)();
}
}
J'espère que cela aidera à montrer d'autres utilisations de ceci que juste ce membre.
Vous devez utiliser this
pour lever l'ambiguïté entre un paramètre / des variables locales et des variables membres.
class Foo
{
protected:
int myX;
public:
Foo(int myX)
{
this->myX = myX;
}
};
Le but principal (ou je peux dire, le seul) du this
pointeur est qu'il pointe vers l'objet utilisé pour appeler une fonction membre.
Sur la base de cet objectif, nous pouvons avoir des cas où seule l'utilisation d'un this
pointeur peut résoudre le problème.
Par exemple, nous devons renvoyer l'objet appelant dans une fonction membre avec l'argument est un même objet de classe:
class human {
...
human & human::compare(human & h){
if (condition)
return h; // argument object
else
return *this; // invoking object
}
};
J'ai trouvé un autre cas intéressant d'utilisation explicite du pointeur «this» dans le livre Effective C ++.
Par exemple, disons que vous avez une fonction const comme
unsigned String::length() const
Vous ne voulez pas calculer la longueur de String pour chaque appel, vous voulez donc le mettre en cache en faisant quelque chose comme
unsigned String::length() const
{
if(!lengthInitialized)
{
length = strlen(data);
lengthInitialized = 1;
}
}
Mais cela ne compilera pas - vous modifiez l'objet dans une fonction const.
L'astuce pour résoudre cela nécessite de convertir ceci en un non-const this :
String* const nonConstThis = (String* const) this;
Ensuite, vous pourrez faire ci-dessus
nonConstThis->lengthInitialized = 1;
length
mutable, ou même le mettre dans une structure imbriquée. Rejeter la constness n'est presque jamais une bonne idée.
const
fonctions membres, il doit l'être mutable
. Sinon, vous compliquez la vie pour vous et pour les autres responsables.