2010-02-27 4 views
4

Désolé si c'est un dupe, je ne peux pas trouver une réponse tout à fait raison.Résolvez les fonctions virtuelles C++ de la classe de base

Je souhaite appeler une fonction d'un membre de la classe de base et la résoudre vers la version de la sous-classe. Je pensais que le déclarer virtuel le ferait, mais ce n'est pas le cas. Voici mon approche:

class GUIWindow 
{ 
public: 
    GUIWindow() 
    { 
     SetupCallbacks(); 
    } 

    virtual void SetupCallbacks() 
    { 
     // This function always called 
    } 
}; 

class GUIListbox : public GUIWindow 
{ 
public: 
    void SetupCallbacks() 
    { 
     // This never called 
    } 
}; 

GUIListbox lb; // GUIWindow::SetupCallbacks is called :(

Qu'est-ce que je fais mal?

Un grand merci

Si

Répondre

8
+0

+1. @sipickles, vous appelez la méthode virtuelle du constructeur de la classe de base - c'est-à-dire avant même que la partie dérivée de l'objet ne soit initialisée. Même s'il a fait appel à la méthode du type dérivé, à quoi s'attendriez-vous que le comportement soit avec des parties non initialisées? :) –

+0

Si c'est ce que le livre dit réellement, alors l'endroit pour ce livre est la poubelle. Ce que le livre semble suggérer, c'est que «vous êtes trop stupide pour apprendre comment cela fonctionne, alors ne le faites jamais». En réalité, le conseil approprié devrait être "Apprenez, comment les fonctions virtuelles fonctionnent pendant la construction et comment les utiliser correctement". Fonction virtuelle * faire du travail * pendant la construction, mais avec quelques détails. – AnT

+0

Ack, merci. C'est évident maintenant! Il est temps de relire mes manuels! – sipi

-1

La réponse rapide est que vous devrez peut-être déclarer un constructeur dans GUIListbox qui appelle SetupCallbacks. La raison est plus compliquée: parce que GUIListbox ne déclare pas de constructeur, lorsque vous déclarez un objet de ce type, le constructeur GUIWindow est appelé. Lorsque le constructeur GUIWindow est en cours d'exécution, l'objet n'est pas encore GUIListbox. Du point de vue du compilateur, il ne devient une GUIListbox qu'une fois que le constructeur (vide) de cette classe démarre. Ainsi, lorsque le premier constructeur s'exécute, les méthodes de GUIWindow sont appelées. IOWs, cela ne fonctionne pas, et ne fonctionnera jamais en C++ comme vous le souhaitez.

Voici une référence décrivant cet écueil en détail: http://www.artima.com/cppsource/nevercall.html.

Et voici une explication de mon favori C++ ressource: http://yosefk.com/c++fqa/inheritance-mother.html#fqa-23.5

1

Votre type dérivé ne se construit pas encore.

Class Cat : Animal 
{ 
//... 
}; 

Lorsque vous créez un créer un chat objet ce qui suit se produit:

  1. Construct animaux
  2. Construct Cat

Lorsque votre objet est hors de portée ou est détruit par suppression si sur le tas, ce qui suit arrive:

  1. Destruct Cat
  2. Destruct animaux

Pour cette raison, vous ne devez pas appeler des fonctions virtuelles dans votre constructeur ou destructor. Vous auriez même un pure virtual function call si vous n'aviez pas d'implémentation dans votre classe de base.

Vous allez devoir:

GUIListbox lb; 
lb.SetupCallbacks(); 

Le point de virtuel est que vous pouvez faire des choses comme:

GuiWindow *lb = new GuiListbox(); 
lb->SetupCallback();//Gets correctly resolved to GuiListBox's version 
0

Le problème est que vous essayez d'appeler la fonction virtuelle former le constructeur. Le constructeur de la classe de base est appelé avant le constructeur de la classe dérivée, de sorte que les "parties dérivées" de l'objet ne sont pas encore créées lorsque le constructeur des classes de base s'exécute.

L'objet se comportera comme un objet du type de classe de base jusqu'à ce que le constructeur de classes de base soit terminé et que le constructeur de classes dérivées démarre. Cela signifie que les appels aux fonctions virtuelles n'appelleront pas les implémentations définies dans la classe dérivée, ils exécuteront simplement la version à partir de la classe de base.

Voir également la FAQ C++ FAQ pour more details et possible workarounds.

3

Lorsque vous appelez des fonctions virtuelles lors de la construction d'une classe C (à savoir alors que le constructeur de C est actif), le mécanisme virtuel fonctionne, mais cela fonctionne dans le mode restreint. La résolution des appels virtuels dans la hiérarchie de classes est limitée par la classe en cours de construction (C). Cela signifie que les appels virtuels seront résolus comme si la classe C était la classe "final" dans la hiérarchie, comme si elle n'avait pas de descendants.

La même chose est vraie pour les destructeurs.

Dans votre exemple, vous appelez une fonction virtuelle du constructeur de la classe GUIWindow. Tant que le constructeur de GUIWindow est actif, son mécanisme virtuel fonctionnera comme s'il n'y avait pas d'autres classes dérivées de GUIWindow. Ce mécanisme ignorera complètement l'existence de la classe GUIListbox. C'est pourquoi la résolution de l'appel virtuel "s'arrête" à GUIWindow et appelle GUIWindow::SetupCallbacks, comme si GUIListbox::SetupCallbacks n'existe pas.

Vous pouvez lire dans C++ FAQ http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.5

Questions connexes