Recherche…


Remarques

Le pointeur this est un mot-clé pour C ++, il n'y a donc pas de bibliothèque nécessaire pour l'implémenter. Et ne pas oublier c'est un pointeur! this Donc, vous ne pouvez pas faire:

 this.someMember();

Lorsque vous accédez à des fonctions membres ou à des variables membres à partir de pointeurs à l'aide du symbole flèche -> :

this->someMember();

Autres liens utiles pour mieux comprendre le pointeur this :

Quel est le pointeur 'this'?

http://www.geeksforgeeks.org/this-pointer-in-c/

https://www.tutorialspoint.com/cplusplus/cpp_this_pointer.htm

ce pointeur

Toutes les fonctions membres non statiques ont un paramètre caché, un pointeur sur une instance de la classe, nommé this ; Ce paramètre est inséré en silence au début de la liste de paramètres et entièrement géré par le compilateur. Lorsqu'un membre de la classe est accédé à l'intérieur d'une fonction membre, il est accessible silencieusement à travers this ; Cela permet au compilateur d'utiliser une fonction membre non statique unique pour toutes les instances et permet à une fonction membre d'appeler d'autres fonctions membres de manière polymorphe.

struct ThisPointer {
    int i;

    ThisPointer(int ii);

    virtual void func();

    int  get_i() const;
    void set_i(int ii);
};
ThisPointer::ThisPointer(int ii) : i(ii) {}
// Compiler rewrites as:
ThisPointer::ThisPointer(int ii) : this->i(ii) {}
// Constructor is responsible for turning allocated memory into 'this'.
// As the constructor is responsible for creating the object, 'this' will not be "fully"
// valid until the instance is fully constructed.

/* virtual */ void ThisPointer::func() {
    if (some_external_condition) {
        set_i(182);
    } else {
        i = 218;
    }
}
// Compiler rewrites as:
/* virtual */ void ThisPointer::func(ThisPointer* this) {
    if (some_external_condition) {
        this->set_i(182);
    } else {
        this->i = 218;
    }
}

int  ThisPointer::get_i() const { return i; }
// Compiler rewrites as:
int  ThisPointer::get_i(const ThisPointer* this) { return this->i; }

void ThisPointer::set_i(int ii) { i = ii; }
// Compiler rewrites as:
void ThisPointer::set_i(ThisPointer* this, int ii) { this->i = ii; }

Dans un constructeur, this peut être utilisé en toute sécurité (implicitement ou explicitement) pour accéder à tout champ déjà initialisé ou à tout champ d'une classe parente; inversement, (implicitement ou explicitement) l'accès à tous les champs qui n'ont pas encore été initialisés, ou à tout champ d'une classe dérivée, est dangereux (la classe dérivée n'étant pas encore construite et ses champs n'étant ni initialisés ni existants). Il est également dangereux d'appeler des fonctions membres virtuelles via this dans le constructeur, comme toutes les fonctions de classe dérivée ne seront pas pris en compte ( en raison de la classe dérivée pas encore en cours de construction, et donc son constructeur ne mettre à jour encore le vtable).


Notez également que, dans un constructeur, le type de l'objet est le type que ce constructeur construit. Cela est vrai même si l'objet est déclaré comme un type dérivé. Par exemple, dans l'exemple ci-dessous, ctd_good et ctd_bad sont de type CtorThisBase dans CtorThisBase() , et tapez CtorThis dans CtorThis() , même si leur type canonique est CtorThisDerived . Au fur et à mesure que les classes dérivées sont construites autour de la classe de base, l'instance passe progressivement par la hiérarchie des classes jusqu'à ce qu'elle devienne une instance entièrement construite du type prévu.

class CtorThisBase {
    short s;

  public:
    CtorThisBase() : s(516) {}
};

class CtorThis : public CtorThisBase {
    int i, j, k;

  public:
    // Good constructor.
    CtorThis() : i(s + 42), j(this->i), k(j) {}

    // Bad constructor.
    CtorThis(int ii) : i(ii), j(this->k), k(b ? 51 : -51) {
        virt_func();
    }

    virtual void virt_func() { i += 2; }
};

class CtorThisDerived : public CtorThis {
    bool b;

  public:
    CtorThisDerived()       : b(true) {}
    CtorThisDerived(int ii) : CtorThis(ii), b(false) {}

    void virt_func() override { k += (2 * i); }
};

// ...

CtorThisDerived ctd_good;
CtorThisDerived ctd_bad(3);

Avec ces classes et fonctions membres:

  • Dans le bon constructeur, pour ctd_good :
    • CtorThisBase est entièrement construit au moment où le constructeur CtorThis est entré. Par conséquent, s est dans un état valide lors de l'initialisation i , et peut donc être consulté.
    • i est initialisé avant que j(this->i) soit atteint. Par conséquent, i dans un état valide lors de l'initialisation j , et peut donc être consulté.
    • j est initialisé avant que k(j) soit atteint. Par conséquent, j est dans un état valide lors de l'initialisation de k , et peut donc être consulté.
  • Dans le mauvais constructeur, pour ctd_bad :
    • k est initialisé après que j(this->k) est atteint. Par conséquent, k est dans un état invalide lors de l'initialisation j , et y accéder entraîne un comportement indéfini.
    • CtorThisDerived n'est construit qu'après la construction de CtorThis . Par conséquent, b est dans un état invalide lors de l'initialisation de k , et l'accès à celui-ci entraîne un comportement indéfini.
    • L'objet ctd_bad est toujours un CtorThis jusqu'à ce qu'il quitte CtorThis() , et ne sera pas mis à jour pour utiliser la CtorThisDerived de CtorThisDerived jusqu'à CtorThisDerived() que CtorThisDerived() . Par conséquent, virt_func() appellera CtorThis::virt_func() , qu'il soit destiné à appeler cela ou CtorThisDerived::virt_func() .

Utiliser le pointeur this pour accéder aux données des membres

Dans ce contexte, l'utilisation du pointeur this n'est pas absolument nécessaire, mais cela rendra votre code plus clair pour le lecteur, en indiquant qu'une fonction ou une variable donnée est un membre de la classe. Un exemple dans cette situation:

// Example for this pointer
#include <iostream>
#include <string>

using std::cout;
using std::endl;

class Class
{
  public:
    Class();
    ~Class();
    int getPrivateNumber () const;
  private:
    int private_number = 42;
};

Class::Class(){}
Class::~Class(){}

int Class::getPrivateNumber() const
{
    return this->private_number;
}

int main()
{
    Class class_example;
    cout << class_example.getPrivateNumber() << endl;
}

Voyez-le en action ici .

Utiliser le pointeur this pour différencier les données de membre et les paramètres

Ceci est une stratégie très utile pour différencier les données des membres des paramètres ... Prenons l'exemple suivant:

// Dog Class Example
#include <iostream>
#include <string>

using std::cout;
using std::endl;

/*
* @class Dog
*   @member name
*       Dog's name
*   @function bark
*       Dog Barks!
*   @function getName
*       To Get Private
*       Name Variable
*/
class Dog
{
 public:
    Dog(std::string name);
    ~Dog();
    void  bark() const;
    std::string  getName() const;
 private:
    std::string name;
};

Dog::Dog(std::string name)
{
    /*
    *  this->name is the
    *  name variable from 
    *  the class dog . and
    *  name is from the 
    *  parameter of the function
    */
    this->name = name; 
}

Dog::~Dog(){}

void Dog::bark() const
{
  cout << "BARK" << endl;   
}

std::string  Dog::getName() const
{
    return this->name;
}


int main()
{
    Dog dog("Max");
    cout << dog.getName() << endl;
    dog.bark();
}

Vous pouvez voir ici dans le constructeur que nous exécutons ce qui suit:

this->name = name; 

Ici, vous pouvez voir que nous associons le nom du paramètre au nom de la variable privée de la classe Dog (this-> name).

Pour voir la sortie du code ci-dessus: http://cpp.sh/75r7

ce Pointer CV-Qualifiers

this peut aussi être qualifié CV, comme n'importe quel autre pointeur. Cependant, étant donné que this paramètre n'est pas répertorié dans la liste de paramètres, une syntaxe spéciale est requise pour cela; les qualificateurs cv sont listés après la liste des paramètres, mais avant le corps de la fonction.

struct ThisCVQ {
    void no_qualifier()                {} // "this" is: ThisCVQ*
    void  c_qualifier() const          {} // "this" is: const ThisCVQ*
    void  v_qualifier() volatile       {} // "this" is: volatile ThisCVQ*
    void cv_qualifier() const volatile {} // "this" is: const volatile ThisCVQ*
};

Comme this s'agit d'un paramètre, une fonction peut être surchargée en fonction de this ou de ces qualificateurs cv .

struct CVOverload {
    int func()                { return    3; }
    int func() const          { return   33; }
    int func() volatile       { return  333; }
    int func() const volatile { return 3333; }
};

Lorsque c'est this const (y compris const volatile ), la fonction est incapable d'écrire à des variables membres à travers elle, que ce soit explicitement ou implicitement. La seule exception à cette règle concerne les variables de membre mutable , qui peuvent être écrites indépendamment de la constance. De ce fait, const est utilisé pour indiquer que la fonction membre ne modifie pas l'état logique de l'objet (la façon dont l'objet apparaît dans le monde extérieur), même s'il modifie l'état physique (la façon dont l'objet se présente sous le capot) ).

L'état logique est la manière dont l'objet apparaît aux observateurs extérieurs. Il n'est pas directement lié à l'état physique et peut même ne pas être stocké en tant qu'état physique. Tant que les observateurs extérieurs ne peuvent voir aucun changement, l'état logique est constant, même si vous retournez chaque bit de l'objet.

L'état physique, également appelé état binaire, est la manière dont l'objet est stocké en mémoire. Ceci est le nitty-graffy de l'objet, les 1 et 0 bruts qui composent ses données. Un objet n'est physiquement constant que si sa représentation en mémoire ne change jamais.

Notez que C ++ base la const sur l'état logique et non sur l'état physique.

class DoSomethingComplexAndOrExpensive {
    mutable ResultType cached_result;
    mutable bool state_changed;

    ResultType calculate_result();
    void modify_somehow(const Param& p);

    // ...

  public:
    DoSomethingComplexAndOrExpensive(Param p) : state_changed(true) {
        modify_somehow(p);
    }

    void change_state(Param p) {
        modify_somehow(p);
        state_changed = true;
    }

    // Return some complex and/or expensive-to-calculate result.
    // As this has no reason to modify logical state, it is marked as "const".
    ResultType get_result() const;
};
ResultType DoSomethingComplexAndOrExpensive::get_result() const {
    // cached_result and state_changed can be modified, even with a const "this" pointer.
    // Even though the function doesn't modify logical state, it does modify physical state
    //  by caching the result, so it doesn't need to be recalculated every time the function
    //  is called.  This is indicated by cached_result and state_changed being mutable.

    if (state_changed) {
        cached_result = calculate_result();
        state_changed = false;
    }

    return cached_result;
}

Notez que si vous pouvez utiliser techniquement const_cast sur this pour le rendre non-cv-qualifié, vous avez vraiment, ne devrait vraiment pas, et devrait utiliser mutable à la place. Un const_cast est susceptible d'appeler un comportement indéfini lorsqu'il est utilisé sur un objet qui est réellement const , alors que mutable est conçu pour être sûr à utiliser. Il est cependant possible que vous rencontriez ceci dans un code extrêmement ancien.

Une exception à cette règle consiste à définir des accesseurs non qualifiés en cv en termes d'accesseurs const ; comme il est garanti que l'objet ne sera pas const si la version non qualifiée de cv est appelée, il n'y a aucun risque d'UB.

class CVAccessor {
    int arr[5];

  public:
    const int& get_arr_element(size_t i) const { return arr[i]; }

    int& get_arr_element(size_t i) {
        return const_cast<int&>(const_cast<const CVAccessor*>(this)->get_arr_element(i));
    }
};

Cela évite la duplication inutile du code.


Comme avec les pointeurs réguliers, si this est volatile (y compris const volatile ), il est chargé depuis la mémoire à chaque fois qu’il est accédé, au lieu d’être mis en cache. Cela a les mêmes effets sur l'optimisation que de déclarer tout autre pointeur volatile , il faut donc faire attention.


Notez que si une instance est cv-qualifié, les seules fonctions de membres , il est autorisé à accéder sont des fonctions membres dont this pointeur est au moins aussi cv-qualifié l'instance elle - même:

  • Les instances non-cv peuvent accéder à toutes les fonctions membres.
  • const instances const peuvent accéder aux fonctions const et const volatile .
  • volatile instances volatile peuvent accéder aux fonctions volatile et const volatile .
  • const volatile instances const volatile peuvent accéder à const volatile fonctions const volatile .

C'est l'un des principes clés de la correction des const .

struct CVAccess {
    void    func()                {}
    void  func_c() const          {}
    void  func_v() volatile       {}
    void func_cv() const volatile {}
};

CVAccess cva;
cva.func();    // Good.
cva.func_c();  // Good.
cva.func_v();  // Good.
cva.func_cv(); // Good.

const CVAccess c_cva;
c_cva.func();    // Error.
c_cva.func_c();  // Good.
c_cva.func_v();  // Error.
c_cva.func_cv(); // Good.

volatile CVAccess v_cva;
v_cva.func();    // Error.
v_cva.func_c();  // Error.
v_cva.func_v();  // Good.
v_cva.func_cv(); // Good.

const volatile CVAccess cv_cva;
cv_cva.func();    // Error.
cv_cva.func_c();  // Error.
cv_cva.func_v();  // Error.
cv_cva.func_cv(); // Good.

ce Pointer Ref-Qualifiers

C ++ 11

De même pour this cv-qualificatifs, nous pouvons également appliquer ref-qualificatifs à *this . Ref-qualificatifs sont utilisés pour choisir entre la sémantique de référence normale et rvalue, ce qui permet au compilateur d'utiliser soit copier ou déplacer la sémantique en fonction qui sont plus appropriés et sont appliqués à *this lieu de this .

Notez que malgré les qualificateurs de référence utilisant la syntaxe de référence, this reste un pointeur. Notez également que les qualificateurs de ref ne modifient pas réellement le type de *this ; il est juste plus facile de décrire et de comprendre leurs effets en les regardant comme s'ils le faisaient.

struct RefQualifiers {
    std::string s;

    RefQualifiers(const std::string& ss = "The nameless one.") : s(ss) {}

    // Normal version.
    void func() &  { std::cout << "Accessed on normal instance "    << s << std::endl; }
    // Rvalue version.
    void func() && { std::cout << "Accessed on temporary instance " << s << std::endl; }

    const std::string& still_a_pointer() &  { return this->s; }
    const std::string& still_a_pointer() && { this->s = "Bob"; return this->s; }
};

// ...

RefQualifiers rf("Fred");
rf.func();              // Output:  Accessed on normal instance Fred
RefQualifiers{}.func(); // Output:  Accessed on temporary instance The nameless one

Une fonction membre ne peut pas avoir de surcharge avec et sans qualificatif ref; le programmeur doit choisir entre l'un ou l'autre. Heureusement, les qualificatifs cv peuvent être utilisés en conjonction avec les qualificatifs ref, ce qui permet de respecter les règles d’ exactitude des const .

struct RefCV {
    void func() &                {}
    void func() &&               {}
    void func() const&           {}
    void func() const&&          {}
    void func() volatile&        {}
    void func() volatile&&       {}
    void func() const volatile&  {}
    void func() const volatile&& {}
};


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow