Quand dois-je utiliser explicitement le pointeur `this`?

97

Quand dois-je écrire explicitement this->memberdans une méthode d'une classe?

dmckee --- chaton ex-modérateur
la source
16
Je suis sûr que c'est une dupe, mais c'est bien sûr insondable. Pas pour la première fois, j'aimerais que ce pointeur s'appelle soi-même!
5
Non seulement cela, je souhaite que ce soit une référence.
rlbond
2
Même. : | Voici pourquoi, au fait: research.att.com/~bs/bs_faq2.html#this
GManNickG
11
Cette méthode ne fonctionne évidemment pas si la personne ne connaît pas la réponse.
Poser le
3
@JohnH .: Hm, on dirait que research.att.com/~bs/c'est maintenant stroustrup.com. Nouveau lien: stroustrup.com/bs_faq2.html#this
GManNickG

Réponses:

118

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

};
Demander
la source
8
Belle utilisation de la déclaration d'utilisation :)
Faisal Vali
3
C'est un cas particulièrement désagréable. J'ai déjà été mordu par ça.
Jason Baker le
5
C'est peut-être une question idiote, mais je ne comprends pas pourquoi cela ipourrait ne pas exister A. Puis-je avoir un exemple?
Cam Jackson
1
@CamJackson J'ai essayé le code sur Visual Studio. les résultats sont les mêmes, peu importe que "this->" existe ou non. Une idée?
Peng Zhang
8
@CamJackson: On peut spécialiser les classes sur le type:template<> struct A<float> { float x; };
Macke
31

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

PaV
la source
1
Je ferais mieux d'utiliser cout << A :: a << endl; au lieu. «this» n'a pas d'importance dans ce cas.
siddhant3s
3
Je préférerais simplement éviter que le nom n'entre en conflit avec des conventions comme "m_a" ou "a_".
Tom
19

Il existe plusieurs raisons pour lesquelles vous devrez peut-être utiliser thisexplicitement le pointeur.

  • Lorsque vous souhaitez transmettre une référence à votre objet à une fonction.
  • Lorsqu'il existe un objet déclaré localement avec le même nom que l'objet membre.
  • Lorsque vous essayez d'accéder aux membres de classes de base dépendantes .
  • Certaines personnes préfèrent la notation pour clarifier visuellement les accès des membres dans leur code.
Avakar
la source
7

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!

Brad Robinson
la source
6
  1. Où une variable membre serait masquée par une variable locale
  2. Si vous voulez simplement indiquer clairement que vous appelez une méthode / variable d'instance


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;
}
bœuf zébré
la source
5

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.

rlbond
la source
Dans votre dernier cas, notez que vous pouvez appeler une constfonction 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));)
Ben Voigt
5

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 thispointeur 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 à thisune 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 mNamepour les membres et namepour les locaux.

John Dibling
la source
4

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->".

Joe Schneider
la source
C'est la seule utilisation this->qui est triviale à éviter (donnez simplement un nom différent à la variable locale). Toutes les utilisations vraiment intéressantes de thisne sont même pas mentionnées par cette réponse.
cmaster - réintégrer monica
4

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.

Moulages

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

Contraignant

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-à-membre

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.

Macke
la source
3

Vous devez utiliser thispour 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; 
  }
};
Brian R. Bondy
la source
2
Non, vous n'en avez pas besoin , vous pouvez l' utiliser . Vous pouvez également utiliser un nom différent pour l'argument de fonction, ce qui présente l'avantage de ne pas avoir deux entités avec le même nom.
cmaster
3

Le but principal (ou je peux dire, le seul) du thispointeur 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 thispointeur 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
    }
};
Trevor
la source
2

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;
Ariel
la source
3
Ou vous pouvez rendre lengthmutable, ou même le mettre dans une structure imbriquée. Rejeter la constness n'est presque jamais une bonne idée.
Richard J.Ross III
3
Veuillez ne pas le faire. Si le membre doit être modifié à partir des constfonctions membres, il doit l'être mutable. Sinon, vous compliquez la vie pour vous et pour les autres responsables.
David Rodríguez - dribeas le