57 votes

Fonction virtuelle C ++ du constructeur

Pourquoi l'exemple suivant imprime "0" et que doit-il changer pour que "1" s'imprime comme prévu?

 #include <iostream>
struct base {
   virtual const int value() const {
      return 0;
   }
   base() {
      std::cout << value() << std::endl;
   }
   virtual ~base() {}
};

struct derived : public base {
   virtual const int value() const {
      return 1;
   }
};

int main(void) {
   derived example;
}
 

90voto

Sean Bright Points 39480

Parce que "base" est construit en premier et n'a pas encore "mûri" en "dérivé". Il ne peut pas appeler de méthodes sur un objet s'il ne peut pas garantir que cet objet est déjà correctement initialisé.

20voto

Charles Bailey Points 244082

Lorsqu'un objet dérivé est en cours de construction, avant que le corps du constructeur de la classe dérivée soit appelé, le constructeur de la classe de base doit être terminé. Avant que le constructeur de la classe dérivée soit appelé, le type dynamique de l'objet en construction est une instance de classe de base et non une instance de classe dérivée. Pour cette raison, lorsque vous appelez une fonction virtuelle à partir d'un constructeur, seules les substitutions de fonction virtuelle de la classe de base peuvent être appelées.

9voto

Tanveer Badar Points 437

En fait, il existe un moyen d’obtenir ce comportement. "Chaque problème de logiciel peut être résolu avec un niveau d'indirection."

 /* Disclaimer: I haven't done C++ in many months now, there might be a few syntax errors here and there. */
class parent
{
public:
     parent( ) { /* nothing interesting here. */ };
protected:
     struct parent_virtual
     {
         virtual void do_something( ) { cout << "in parent."; }
     };

     parent( const parent_virtual& obj )
     {
          obj.do_something( );
     }
};

class child : public parent
{
protected:
     struct child_virtual : public parent_virtual
     {
         void do_something( ) { cout << "in child."; }
     };
public:
      child( ) : parent( child_virtual( ) ) { }
};
 

3voto

Vinay Points 3310

Vous ne devez pas appeler les méthodes virtuelles depuis le constructeur. Au lieu de cela, vous pouvez les appeler après la construction de l'objet.

Votre code peut être réécrit comme suit

 struct base {
   virtual const int value() const {
      return 0;
   }
   base() {
      /* std::cout << value() << std::endl; */
   }
   virtual ~base() {}
};

struct derived : public base {
   virtual const int value() const {
      return 1;
   }
};

int main(void) {
   derived example;
   std::cout << example.value() << std::endl;
}
 

3voto

La question de savoir comment il fonctionne est un élément de FAQ.

En résumant, tandis que la catégorie T est en cours de construction, le type dynamique est T, ce qui empêche les appels virtuels de classe dérivée de la fonction implémentations, qui si elle est autorisée pourrait exécuter du code avant l'invariant de classe a été établi (un problème commun en Java et en C#, mais le C++ est sûr à ce sujet).

La question de la façon de faire de la classe dérivée d'initialisation spécifique à un constructeur de classe de base est également un élément de FAQ, directement à la suite de la mentionné précédemment.

En résumant, à l'aide de statique ou dynamique polymorphisme sur peut passer de la fonction correspondante implémentations jusqu'à la base le constructeur de la classe (ou classe).

Une façon particulière de le faire est de passer d'une “usine de pièces d'objet” , où cet argument peut être affichée par défaut. Par exemple, un général, Button classe peut passer à un bouton de la création de la fonction de l'API jusqu'à son Widget constructeur de classe de base, de sorte que le constructeur peut créer le bon niveau API objet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X