2017-09-01 4 views
3

Voici le contexte: Un Model a un (pointeur vers) Parameter et un output. Model et Parameter sont des classes abstraites. Nous utilisons des pointeurs de type Model* pour manipuler diverses classes dérivées (concrètes) de Model, dont les pointeurs vers Parameter pointent dynamiquement vers des instances de diverses classes dérivées (concrètes) de Parameter.Polymorphisme: cette utilisation (potentiellement intensive) de static_cast est-elle fatale?

Voici une version simplifiée des classes à titre d'exemple. Je sais new devrait être évité ou au moins suivi par delete, mais j'ai omis des lignes de code hors-sujet (tels que les destructeurs).

// Abstract classes 
class Parameter { 
public: 
    virtual void reinitialize() = 0; 
}; 

class Model{ 
public: 
    Model(Parameter *argt){ ptr_param = argt; } 
    virtual void computeModelOutput() = 0; 
    double output; 
    Parameter *ptr_param; 
}; 

// Concrete classes 
class ACertainKindOfParameter : public Parameter{ 
public: 
    ACertainKindOfParameter(int argt){ value = argt; } 
    virtual void reinitialize(){ value = 1; } 
    int value; 
}; 

class ACertainKindOfModel : public Model{ 
public: 
    ACertainKindOfModel(int argt) : Model(new ACertainKindOfParameter(argt)){} 
    virtual void computeModelOutput(){ 
     output = 10.0 + (double)(static_cast<ACertainKindOfParameter*>(ptr_param)->value); 
    } 
}; 

int main(){ 
    ACertainKindOfModel myModel{5}; 
    Model *ptr_model = &myModel; 
    ptr_model->computeModelOutput(); 
    std::cout << ptr_model->output << std::endl; // 15 
} 

Ce qui me dérange dans ce code est que ACertainKindOfModel n'a pas accès direct à value, donc j'ai besoin apparemment d'utiliser static_cast. Un vrai Model aurait bien sûr un vecteur de par ex. 50 Parameter s, pas seulement un, ce qui signifie 50 static_cast chaque fois que le output est calculé (ou toute autre action reposant sur des paramètres). Cela ne me semble pas être une bonne pratique, mais j'ai peut-être tort. Voyez-vous un défaut dans la conception?

Note: Je pensais que de faire Parameter un modèle de classe, mais il ne semble pas être une option valable parce que les méthodes de Parameter diffèrent profondément lorsque différents types de value sont considérés. Dans l'exemple simple ci-dessus, value est du type int, mais dans une autre classe dérivée de Parameter, il pourrait être du type défini par l'utilisateur, par ex. Color avec seulement trois valeurs possibles R, G et B, et reinitialize() serait très différent de value = 1. Un getter() virtuel en Parameter serait génial mais ne fonctionnerait pas non plus, à cause d'un type de retour conflictuel dans la redéfinition.

+1

Qu'allez-vous faire avec de la valeur? Si c'est un calcul, regardez dans [Visitor Pattern] (https: //en.wikipedia.org/wiki/Visitor_pattern) donc aucune autre classe ne doit savoir quelle est la valeur. Si c'est l'affichage, pensez à ajouter un getter virtuel qui renvoie la valeur sous une forme neutre comme une chaîne. – user4581301

+1

@ user4581301 En effet, 'value' est principalement pour le calcul comme indiqué dans le morceau de code que j'ai écrit, et il pourrait aussi apparaître dans des conditions telles que' if (value == R) ', etc. (en particulier où' value' est pas un nombre). Merci pour vos conseils sur le modèle Visitor. – Georg

+0

* "Je sais que le neuf devrait être évité" *. Alors utilisez des pointeurs intelligents: et encore moins de ligne de code. – Jarod42

Répondre

3

Il existe plusieurs approches pour faire ce nettoyeur. Si Model n'a pas besoin d'accéder à ptr_param, vous pouvez le supprimer de Model et le stocker dans chaque classe dérivée, avec le type correct.

Ou vous pouvez résumer la static_cast dans une fonction de lecture dans chaque classe de modèle:

ACertainKindOfParameter *getParam() const { return static_cast<ACertainKindOfParameter *>(ptr_param); } 

Vous pouvez combiner les deux techniques. Définissez le paramètre dans la classe de modèle dérivée et utilisez un type de retour covariant pour autoriser l'accès de classe de base Model. De Model, déclarer un getter:

virtual Parameter *getParam() const = 0; 

Ensuite, dans chaque modèle, déclarer une substitution covariant:

virtual ACertainKindOfParameter *getParam() const override { return ptr_param; } 

qui suppose ptr_param est déclarée à l'intérieur de ACertainKindOfModel. Si ce n'est pas, vous devrez appliquer le static_cast comme ci-dessus.

Ou vous pouvez enregistrer le résultat du static_cast dans la fonction compute pour éviter de devoir l'utiliser plusieurs fois.

+0

Ma conclusion personnelle: J'ai essayé d'innombrables choses et cela se résume principalement à l'initiale 'static_cast'. Faire 'Parameter' une classe de template a presque fonctionné mais a finalement échoué parce que la déduction d'argument de modèle [ne fonctionne pas pour déclarer des pointeurs] (https://stackoverflow.com/questions/46085683/). – Georg