web-dev-qa-db-fra.com

Où le pointeur «this» est-il stocké dans la mémoire de l'ordinateur?

Où exactement le pointeur "this" est-il stocké en mémoire? Est-il alloué sur la pile, dans le segment de mémoire ou dans le segment de données?

#include <iostream>
using namespace std;

class ClassA
{
    int a, b;

    public:
        void add()
        {
            a = 10;
            b = 20;
            cout << a << b << endl;
        }
};

int main()
{
    ClassA obj;
    obj.add();
    return 0;
}

Dans le code ci-dessus, j'appelle la fonction membre add() et l'objet récepteur est passé implicitement comme le pointeur 'this'. Où this est-il stocké en mémoire?

63

D'autres réponses ont fait un très bon travail expliquant comment un compilateur typique implémente this (en le passant comme premier paramètre implicite à la fonction).

Je pense qu'il est également utile de voir ce que la spécification ISO C++ dit explicitement à ce sujet. Selon la spécification ISO C++ 03, §9.3.2/1:

Dans le corps d'une fonction membre non statique (9.3), le mot clé this est une expression non lvalue dont la valeur est l'adresse de l'objet pour lequel la fonction est appelée.

Il est important de noter que this est pas une variable - c'est une expression , de la même manière que l'expression 1 + 2 * 3 est une expression. La valeur de cette expression peut être stockée à peu près n'importe où. Le compilateur pourrait le mettre sur la pile et le passer comme paramètre implicite à une fonction, ou il pourrait le mettre dans un registre, et il pourrait éventuellement le mettre dans le tas ou dans le segment de données. La spécification C++ donne délibérément à l'implémentation une certaine flexibilité ici.

Je pense que la réponse "avocat de la langue" est "ceci est complètement défini par l'implémentation, et de plus this n'est techniquement pas un pointeur, mais une expression qui évalue un pointeur".

J'espère que cela t'aides!

63
templatetypedef

La manière la plus simple est de considérer this comme un argument supplémentaire caché qui est toujours passé automatiquement.

Donc, une méthode fictive comme:

size_t String::length(void) const
{
  return strlen(m_string);
}

est en fait plus comme ça sous le capot:

size_t String__length(const String *this)
{
  return strlen(this->m_string);
}

et un appel comme:

{
  String example("hello");
  cout << example.length();
}

devient quelque chose comme:

cout << String__length(&example);

Notez que la transformation ci-dessus est simplifiée, espérons-le pour clarifier un peu mon point. Pas besoin de remplir les commentaires avec "whaaa, où est le rassemblement pour la surcharge de méthode, hein?" - objection de type, s'il vous plaît. :)

Cela transforme la question en "où sont stockés les arguments?", Et la réponse est bien sûr "cela dépend". :)

Il se trouve souvent sur la pile, mais il peut également se trouver dans des registres ou tout autre mécanisme que le compilateur considère comme bon pour l'architecture cible.

78
unwind

this est généralement passé comme un argument caché de la méthode (la seule différence entre les différentes conventions d'appel est comment).

Si vous appelez:

myClass.Method(1, 2, 3);

Le compilateur génère le code suivant:

Method(&myClass, 1, 2, 3);

Où le premier paramètre est en fait le pointeur vers this.

Vérifions le code suivant:

class MyClass
{
private:
    int a;

public:
    void __stdcall Method(int i)
    {
        a = i;
    }
};

int main(int argc, char *argv[]) 
{
    MyClass myClass;
    myClass.Method(5);

    return 0;
}

En utilisant __stdcall J'ai forcé le compilateur à passer tous les paramètres à travers la pile. Si vous démarrez ensuite le débogueur et inspectez le code Assembly, vous trouverez quelque chose comme ceci:

     myClass.Method(5);
00AA31BE  Push        5  
00AA31C0  lea         eax,[myClass]  
00AA31C3  Push        eax  
00AA31C4  call        MyClass::Method (0AA1447h)  

Comme vous le voyez, le paramètre de la méthode est passé à travers la pile, puis l'adresse de myClass est chargée dans le registre eax et à nouveau poussée sur la pile. En d'autres termes, this est traité comme un paramètre régulier de cette méthode.

33
Spook

this est une valeur r (vous ne pouvez pas prendre son adresse), donc elle n'occupe pas (nécessairement) de mémoire du tout. Selon le compilateur et l'architecture cible, il sera souvent dans un registre: i0 sur un Sparc, ECX avec MSVC sur Intel, etc. Lorsque l'optimiseur est actif, il peut même se déplacer. (Je l'ai vu dans différents registres avec MSVC).

18
James Kanze

this se comporte principalement comme un argument de fonction, et en tant que tel sera stocké sur la pile ou - si les conventions d'appel binaires de l'architecture le permettent - dans un registre.

9
MvG

this n'est pas stocké à un emplacement bien défini! L'objet vers lequel il pointe est stocké quelque part et possède une adresse bien définie, mais l'adresse elle-même n'a pas d'adresse personnelle spécifique. Il est communiqué autour du programme. Non seulement cela, mais il peut y avoir de nombreuses copies de ce pointeur.

Dans la fonction imaginaire init suivante, l'objet s'enregistre pour recevoir des événements et des rappels de temporisation (en utilisant des objets sources d'événements imaginaires). Ainsi, après l'enregistrement, il existe deux copies supplémentaires de this:

void foo_listener::init()
{
   g_usb_events.register(this); // register to receive USB events
   g_timer.register(this, 5);   // register for a 5 second timer
}

Dans le cas d'une chaîne d'activation de fonction, il y aura également plusieurs copies du pointeur this. Supposons que nous ayons un objet obj et appelons sa fonction foo. Cette fonction appelle la fonction bar du même objet et bar appelle une autre fonction appelée update. Chaque niveau d'activation de fonction a le pointeur this. Il est stocké dans un registre de machine ou dans un emplacement mémoire dans le cadre de pile de l'activation de la fonction.

0
Kaz