2009-09-21 11 views
4

J'ai une classe de base que je veux ressembler à ceci:Comment puis-je obtenir un comportement polymorphique dans un constructeur C++?

class B 
{ 
    // should look like: int I() { return someConst; } 
    virtual int I() = 0; 
    public B() { something(I()); } 
} 

Le point étant de forcer les classes dérivées de passer outre I et le forcer à appeler quand chaque objet est construit. Cela sert à faire de la comptabilité et j'ai besoin de savoir quel type d'objet est en cours de construction (mais je traite l'objet actuel comme la classe de base).

Cela ne fonctionne pas car C++ ne vous permet pas d'appeler une fonction virtuelle abstraite du constructeur.

Existe-t-il un moyen d'obtenir le même effet?


Sur la base de this link il semble que la réponse est qu'il n'y a aucun moyen d'obtenir ce que je veux. Cependant ce qu'il dit est:

La réponse courte est: non. Une classe de base ne sait rien de la classe dont elle est dérivée - et c'est une bonne chose aussi. [...] En d'autres termes, l'objet ne devient officiellement une instance de Derived1 que lorsque le constructeur Derived1 :: Derived1 commence.

Cependant, dans mon cas, je ne veux pas savoir ce qu'il est mais ce qu'il va devenir . En fait, je ne m'inquiète même pas de ce que je récupère tant que l'utilisateur peut (après coup) le mapper à une classe. Donc, je pourrais même utiliser quelque chose comme un pointeur de retour et s'en tirer avec.

(maintenant à la lecture de ce lien)

+0

Lisez ceci: http://stackoverflow.com/questions/1425695/1426834. Vous pouvez utiliser PIMPL. –

Répondre

6

Vous ne pouvez pas appeler des méthodes virtuelles à partir du constructeur (ou pour être plus précis, vous pouvez les appeler, mais vous finirez par appeler la fonction membre de la classe en cours de construction), le problème est que l'objet dérivé n'existe pas encore à ce moment. Il y a très peu de choses que vous pouvez faire à ce sujet, appeler les méthodes virtuelles du constructeur de façon polymorphique est tout simplement hors de question.

Vous devez repenser votre conception en transmettant la constante en tant qu'argument au constructeur, par exemple.

class B 
{ 
public: 
    explicit B(int i) 
    { 
     something(i); 
    } 
}; 

Voir C++ faq pour plus d'informations. Si vous vraiment voulez appeler des fonctions virtuelles pendant la construction, read this. Peut-être utiliser une méthode d'usine statique sur chaque type dérivé?

+0

Vous vouliez dire "vous ne pouvez pas ..."? – Naveen

+0

Oui, c'était une faute de frappe, merci Naveen :) – avakar

+0

Avoir une méthode init() que vous appelez après le constructeur est une solution de contournement. Mais alors vous devez changer partout où la classe est construite - ce qui est bien si vous utilisez une usine. –

0

C'est la manière habituelle de construire des objets exotiques (lire: ceux avec des exigences d'initialisation très spécifiques) dans .NET, que j'ai appris à apprécier.

class Base 
{ 
    protected Base(int i) 
    { 
    // do stuff with i 
    } 
} 

class Derived : public Base 
{ 
    private Derived(int i) 
    : Base(i) 
    { 
    } 

    public Derived Create() 
    { 
    return new Derived(someConstantForThisDerivedType); 
    } 
} 

Appel des méthodes virtuelles dans les constructeurs de base est généralement mal vus, comme vous ne pouvez jamais être certain du comportement d'une méthode particulière, et (comme quelqu'un d'autre a déjà indiqué) les constructeurs dérivés n'auront pas encore été appelé.

0

Cela ne fonctionnera pas comme la classe dérivée n'existe pas encore lorsque le constructeur de la classe de base est exécutée:

class Base 
{ 
public: 
    Base() 
    { 
     // Will call Base::I and not Derived::I because 
     // Derived does not yet exist. 
     something(I()); 
    } 

    virtual ~Base() = 0 
    { 
    } 

    virtual int I() const = 0; 
}; 

class Derived : public Base 
{ 
public: 
    Derived() 
    : Base() 
    { 
    } 

    virtual ~Derived() 
    { 
    } 

    virtual int I() const 
    { 
     return 42; 
    } 
}; 

vous pourriez plutôt ajouter les arguments au constructeur de la classe de base:

class Base 
{ 
public: 
    explicit Base(int i) 
    { 
     something(i); 
    } 

    virtual ~Base() = 0 
    { 
    } 
}; 

class Derived : public Base 
{ 
public: 
    Derived() 
    : Base(42) 
    { 
    } 

    virtual ~Derived() 
    { 
    } 
}; 

Ou si vous aimez vraiment OOP vous pouvez également créer quelques classes supplémentaires:

class Base 
{ 
public: 
    class BaseConstructorArgs 
    { 
    public: 
     virtual ~BaseConstructorArgs() = 0 
     { 
     } 

     virtual int I() const = 0; 
    }; 

    explicit Base(const BaseConstructorArgs& args) 
    { 
     something(args.I()); 
    } 

    virtual ~Base() = 0 
    { 
    } 
}; 

class Derived : public Base 
{ 
public: 
    class DerivedConstructorArgs : public BaseConstructorArgs 
    { 
    public: 
     virtual ~DerivedConstructorArgs() 
     { 
     } 

     virtual int I() const 
     { 
      return 42; 
     } 
    }; 

    Derived() 
    : Base(DerivedConstructorArgs()) 
    { 
    } 

    virtual ~Derived() 
    { 
    } 
}; 
0

Ce dont vous avez besoin est two-phase construction. Utilisez le remède Universal Programmer: Ajouter une autre couche d'indirection.