web-dev-qa-db-fra.com

Que peut provoquer un appel de fonction virtuelle pure en C++?

J'enseigne une classe de programmation C++ et j'ai vu suffisamment de classes d'erreurs pour savoir comment diagnostiquer les bogues C++ courants. Cependant, il existe un type d'erreur majeur pour lequel mon intuition n'est pas particulièrement bonne: quelles erreurs de programmation provoquent des appels vers des fonctions virtuelles pures? L'erreur la plus courante que j'ai rencontrée et qui provoque ceci est l'appel d'une fonction virtuelle à partir d'un constructeur ou d'un destructeur de classe de base. Y a-t-il d'autres personnes dont je devrais être au courant lorsque je participe au débogage du code de l'étudiant?

33
templatetypedef

"L’erreur la plus courante que j’ai vue qui provoque cela est l’appel d’une fonction virtuelle à partir d’un constructeur ou d’un destructeur de classe de base."

Lorsqu'un objet est construit, le pointeur sur la table de répartition virtuelle vise initialement la superclasse la plus élevée. Il est mis à jour uniquement lorsque la construction des classes intermédiaires est terminée. Vous pouvez donc appeler accidentellement l’implémentation virtuelle pure jusqu’à ce qu’une sous-classe - avec sa propre implémentation de fonction prépondérante - achève la construction. Cela pourrait être la sous-classe la plus dérivée, ou n'importe où entre les deux.

Cela peut arriver si vous suivez un pointeur sur un objet partiellement construit (par exemple, dans des conditions de concurrence critique en raison d'opérations asynchrones ou threadées).

Si un compilateur a des raisons de penser connaître le type réel sur lequel pointe un pointeur vers une classe de base, il peut raisonnablement contourner la répartition virtuelle. Vous pouvez le confondre en faisant quelque chose avec un comportement indéfini, tel qu'un casting réinterprété.

Lors de la destruction, la table de répartition virtuelle doit être inversée au fur et à mesure que les classes dérivées sont détruites, afin que l'implémentation virtuelle pure puisse à nouveau être invoquée.

Après la destruction, l'utilisation continue de l'objet via des pointeurs ou des références "en suspens" peut appeler la fonction virtuelle pure, mais il n'y a pas de comportement défini dans de telles situations.

28
Tony Delroy

Voici quelques cas dans lesquels un appel virtuel pur peut avoir lieu.

  1. Utilisation d'un pointeur suspendu - le pointeur n'est pas un objet valide et la table virtuelle sur laquelle il pointe est simplement une mémoire aléatoire pouvant contenir NULL
  2. Mauvaise conversion utiliser un static_cast pour le type incorrect (ou une conversion de style C) peut également empêcher l’objet que vous pointez d’avoir les bonnes méthodes dans sa table virtuelle (dans ce cas du moins, il s'agit réellement de est une table virtuelle contrairement à l’option précédente).
  3. DLL a été déchargé - Si l'objet que vous conservez a été créé dans un fichier objet partagé (DLL, so, sl) qui a été déchargé à nouveau, la mémoire peut être mise à zéro maintenant.
8
Motti

Cela peut se produire par exemple lorsque la référence ou le pointeur sur un objet pointe vers un emplacement NULL et que vous utilisez la référence ou le pointeur de l'objet pour appeler une fonction virtuelle dans la classe. Par exemple: 

std::vector <DerivedClass> objContainer;  
if (!objContainer.empty()) 
   const BaseClass& objRef = objContainer.front();  
// Do some processing using objRef and then you erase the first
// element of objContainer
objContainer.erase(objContainer.begin());   
const std::string& name = objRef.name();  
// -> (name() is a pure virtual function in base class, 
// which has been implemented in DerivedClass).

À ce stade, l'objet stocké dans objContainer [0] n'existe pas. Lorsque la table virtuelle est indexée, aucun emplacement de mémoire valide n'est trouvé. Par conséquent, une erreur d'exécution est émise en indiquant "fonction virtuelle pure appelée". 

0
cppcoder