Utiliser le pointeur this en C++

Muhammad Husnain 12 octobre 2023
  1. Utiliser le pointeur this en C++
  2. La différence entre this et *this en C++
Utiliser le pointeur this en C++

Dans ce tutoriel, nous allons d’abord comprendre le concept du pointeur this.

Ensuite, nous démontrerons ses utilisations à l’aide d’exemples. Enfin, nous verrons en quoi this diffère de this.

Utiliser le pointeur this en C++

Le pointeur this est simplement un pointeur implicitement disponible qui, dans la portée d’une fonction membre de classe non statique, fait référence ou pointe vers l’objet appelant. Si vous n’avez pas compris la définition, regardons quelques principes fondamentaux pour comprendre le concept.

Nous savons que tous les membres de données non statiques ont une copie distincte pour chaque objet. Cependant, le code des fonctions membres est partagé entre tous les objets.

Tous les objets d’une même classe accèdent à la même définition de fonction à partir du segment de code lorsqu’ils doivent l’invoquer. Le compilateur a besoin de connaître les informations de l’objet appelant (qui appelle actuellement une fonction membre) pour accéder ou mettre à jour le bon membre de données spécifique à cet objet appelant particulier.

Donc, étant donné que les définitions de fonction sont les mêmes, comment le compilateur obtient-il les informations sur l’objet appelant ?

La réponse simple est que lors de la compilation du programme, le compilateur ajoute automatiquement un paramètre de pointeur implicite caché dans les fonctions membres. Ce pointeur implicite est appelé this pointeur.

Chaque fois qu’une fonction membre est invoquée via un objet de classe particulier, l’objet appelant est automatiquement fourni en tant qu’argument caché au pointeur this. Maintenant, le compilateur peut utiliser ce pointeur pour accéder ou modifier le bon membre de données spécifique à l’objet appelant.

Noter
Le pointeur this n’est pas nécessaire avec les membres statiques de la classe car ces membres (fonctions et membres de données) sont partagés entre tous les objets.

La mise à jour ou l’accès aux membres statiques n’est spécifique à aucun objet. On peut même accéder à ces membres sans créer d’objet de la classe.

Par conséquent, le compilateur n’utilise pas l’opérateur this avec ces membres.

Examinons l’exemple de code pour comprendre pourquoi nous disons que le compilateur utilise implicitement le pointeur this et quand il est pratique.

#include <iostream>
#include <string>
using namespace std;

class Person {
 private:
  string Name;

 public:
  Person(string Name) {
    this->Name = Name;  // this->Name is the private member for this object
  }
  void PrintName() { cout << this->Name << endl; }
  void PrintName1() { cout << Name << endl; }
};
int main() {
  Person P("Alexa");
  P.PrintName();
  P.PrintName1();
  return 0;
}

L’exemple de code ci-dessus définit une classe Person avec deux fonctions membres différentes pour imprimer le membre de données privé Name. La première instruction de la fonction main() génère un objet Person et transmet Alexa comme argument au constructeur.

Désormais, this->Name dans le constructeur aide le compilateur à faire la distinction entre le paramètre local Name et le membre de données privé Name.

Le code suivant de main() appelle printName() et printName1() via P (c’est-à-dire que P devient l’objet appelant).

Production:

Alexa
Alexa

La sortie des deux fonctions est la même. C’est parce que le compilateur précède implicitement this-> avec Name dans le PrintName1().

La différence entre this et *this en C++

Jusqu’à présent, nous avons bien compris que this est un pointeur vers un objet. Pour un obj de type Person, this est de type Person*.

Une autre chose à retenir est que le pointeur this est toujours une rvalue, qui ne peut pas être modifiée. Cependant, *this déréférence le pointeur this.

Après avoir fait un tour d’horizon suffisant, regardons un exemple de code pour comprendre la différence entre this et *this.

#include <iostream>
using namespace std;

class Counter {
 private:
  int Count;

 public:
  Counter() { this->Count = 0; }
  void IncreaseCount() { Count++; }
  void PrintCount() { cout << this->Count << endl; }
  Counter* GetCount_Pointer() { return this; }
  Counter GetCount_Copy() { return *this; }
  Counter& GetCount_Reference() { return *this; }
};
int main() {
  // Section-A
  cout << "Sectio-A" << endl;
  Counter C1;
  C1.IncreaseCount();
  Counter* CounterPtr =
      C1.GetCount_Pointer();  // CounterObj will be pointing to C1
  CounterPtr->IncreaseCount();
  C1.PrintCount();

  // Section-B
  cout << "Section-B" << endl;
  Counter C2;
  C2 = C1.GetCount_Copy();
  C1.IncreaseCount();
  C1.PrintCount();
  C2.PrintCount();

  // Section-C
  cout << "Section-B" << endl;
  Counter& CounterRef = C1.GetCount_Reference();
  CounterRef.PrintCount();

  return 0;
}

Cet extrait de code crée une classe Counter avec plusieurs méthodes pour les méthodes GetCount. La première méthode (c’est-à-dire GetCount_Pointer) renvoie la valeur de this pointeur qui est simplement une adresse de l’objet appelant.

La méthode GetCount_Copy renvoie *this alors que le type de retour est un objet de type Counter. Ainsi, cette fonction renverra une copie complète de l’objet appelant, ce qui signifie que la modification de la copie renvoyée n’affectera pas la copie d’origine.

La dernière méthode GetCount_Reference renvoie également *this, mais le type de retour de cette méthode est un objet de référence (c’est-à-dire Counter& ). Au lieu de créer une nouvelle copie complète, le compilateur renverra un alias ou une référence à l’objet d’origine.

Toute modification apportée via l’alias sera également reflétée dans l’objet d’origine.

Avant de discuter du code du pilote principal, examinons la sortie du programme.

Production:

Sectio-A
2
Section-B
3
2
Section-B
3

La section-A de la méthode main déclare d’abord l’objet C1 de type Counter et incrémente son Count de IncreaseCount(). Plus tard, il initialise un pointeur CounterPtr avec l’adresse retournée par C1.GetCount_Pointer().

Maintenant, le CounterPtr pointe vers C1. Par conséquent, l’appel de la fonction d’incrémentation par CounterPtr modifie également le C1.

C2 = C1.GetCount_Copy() dans la section-B copie en profondeur tout le contenu actuel de C1 vers C2 car C1.GetCount_Copy() est remplacé par une copie de C1. Par conséquent, l’augmentation du nombre de C1 n’a pas d’effet sur C2.

La section-C déclare une variable de référence de type Counter et l’initialise avec ce qui est renvoyé par C1.GetCount_Reference(). Comme C1.GetCount_Reference() renvoie un alias de C1, par conséquent, CounterRef devient un autre nom pour C1.

L’instruction Counter& CounterRef = C1.GetCount_Reference(); devient logiquement équivalent à Counter& CounterRef = C1.

Muhammad Husnain avatar Muhammad Husnain avatar

Husnain is a professional Software Engineer and a researcher who loves to learn, build, write, and teach. Having worked various jobs in the IT industry, he especially enjoys finding ways to express complex ideas in simple ways through his content. In his free time, Husnain unwinds by thinking about tech fiction to solve problems around him.

LinkedIn

Article connexe - C++ Pointer