Comment C ++ stocke-t-il les fonctions et les objects en mémoire?

Disons que nous avons une classe

class A { int x; public: void sayHi() { cout<sayHi(); } 

Le code ci-dessus sera compilé sur Turbo C (où j’ai testé) et affiche Hi en sortie.

Je m’attendais à un crash car a est NULL . De plus si je fais la fonction sayHi() virtuelle, elle dit

 Abnormal temination(Segmentation fault in gcc) 

Je sais que cela dépend en grande partie de la mise en œuvre, mais si quelqu’un pouvait nous éclairer sur une mise en œuvre ou simplement en donner un aperçu, ce serait vraiment bien.

De toute évidence, le code a un comportement indéfini, c’est-à-dire que tout ce que vous obtenez est un hasard. Cela dit, le système n’a pas besoin de connaître l’object lorsqu’il appelle une fonction membre non virtuelle: il peut simplement être appelé en fonction de la signature. De plus, si une fonction membre n’a pas besoin d’accéder à un membre, elle n’a pas vraiment besoin d’un object et peut simplement s’exécuter. C’est ce que vous avez observé lorsque le code imprimait une sortie. La question de savoir si le système est mis en œuvre n’est pas définie, c’est-à-dire que rien ne dit que cela fonctionne.

Lors de l’appel d’un système de type de fonction virtuelle, il commence par examiner un enregistrement d’informations de type associé à l’object. Lors de l’appel d’une fonction virtuelle sur un pointeur NULL , aucune information de ce type n’existe et toute tentative d’y accéder entraîne probablement une sorte de blocage. Pourtant, cela n’est pas nécessaire, mais cela convient à la plupart des systèmes.

BTW, main() renvoie toujours int .

En C ++, les méthodes d’une classe ne sont pas stockées dans les instances de cette classe. Ce sont simplement des fonctions “spéciales” qui acceptent de manière transparente le pointeur this en plus des arguments spécifiés par le programmeur.

Dans votre cas, la méthode sayHi() ne fait référence à aucun des champs de classe. Par conséquent, le pointeur this (qui est NULL ) n’est jamais suivi.

Ne vous y trompez pas, il s’agit toujours d’un comportement indéfini. Votre programme peut choisir d’envoyer des courriels malveillants à votre liste de contacts lorsque vous appelez cela. Dans ce cas particulier, il fait la pire chose et semble fonctionner.

Le cas de méthode virtual a été ajouté depuis que j’ai répondu à la question, mais je ne vais pas affiner ma réponse, car elle est incluse dans les réponses des autres.

En général, la disposition d’un object instancié à partir d’une classe sans super classes ni fonctions virtuelles est la suivante:

 * - v_ptr ---> * pTypeInfo | |- pVirtualFuncA | |- pVirtualFuncB |- MemberVariableA |- MemberVariableB 

v_ptr est un pointeur sur la v-table – qui contient les adresses des fonctions virtuelles et les données RTTI de l’object. Les classes sans fonctions virtuelles n’ont pas de v-tables.

Dans votre exemple ci-dessus, la class A n’a pas de méthodes virtuelles et donc pas de v-table. Cela signifie que l’implémentation de sayHi() à appeler peut être déterminée lors de la compilation et est invariante.

Le compilateur génère un code qui définit le point implicite de this pointeur sur a , puis passe au début de sayHi() . Puisque l’implémentation n’a pas besoin du contenu de l’object, le fait qu’il fonctionne lorsque le pointeur est à NULL est une heureuse coïncidence.

Si vous sayHi() rendre sayHi() virtuel, le compilateur ne peut pas déterminer l’implémentation à appeler au moment du compilateur; il génère donc un code qui recherche l’adresse de la fonction dans la table virtuelle et l’appelle. Dans votre exemple où a est NULL , le compilateur lit le contenu de l’adresse 0 , ce qui provoque l’abandon.

Si vous appelez une méthode non virtuelle d’une classe, il suffit au compilateur de savoir à quelle classe appartient la fonction et de déréférencer – même si un pointeur NULL – une classe pour appeler la méthode, le compilateur obtient ces informations. La méthode sayHi () est à peu près juste une fonction qui prend un pointeur sur l’instance de la classe en tant que paramètre caché. Ce pointeur est NULL, mais cela n’a pas d’importance si vous ne référencez aucun atsortingbut dans la méthode.

Au moment où vous rendez cette méthode virtuelle, la situation change. Le compilateur ne sait plus quel code est associé à la méthode au moment de la compilation et doit le comprendre au moment de l’exécution. Ce qu’il fait est qu’il regarde dans une table qui contient essentiellement des pointeurs de fonction pour toutes les méthodes virtuelles; cette table est associée à l’occurrence de la classe, elle examine donc une partie de la mémoire par rapport au pointeur NULL et se bloque par conséquent dans ce cas.