2010-06-08 18 views
0

J'ai un ensemble de classes dérivées toutes d'une classe de base commune. Je veux utiliser ces classes de manière polymorphique. L'interface définit un ensemble de méthodes getter dont les valeurs de retour sont constantes dans une classe dérivée donnée, mais varient d'une classe dérivée à l'autre. .: par exemple Méthodes virtuelles C++ pour les attributs propres à la classe ou la structure externe

enum AVal 
{ 
    A_VAL_ONE, 
    A_VAL_TWO, 
    A_VAL_THREE 
}; 

enum BVal 
{ 
    B_VAL_ONE, 
    B_VAL_TWO, 
    B_VAL_THREE 
}; 

class Base 
{ 
    //... 
    virtual AVal getAVal() const = 0; 
    virtual BVal getBVal() const = 0; 
    //... 
}; 

class One : public Base 
{ 
    //... 
    AVal getAVal() const { return A_VAL_ONE }; 
    BVal getBVal() const { return B_VAL_ONE }; 
    //... 
}; 

class Two : public Base 
{ 
    //... 
    AVal getAVal() const { return A_VAL_TWO }; 
    BVal getBVal() const { return B_VAL_TWO }; 
    //... 
}; 

etc.

Est-ce une façon courante de faire les choses? Si la performance est un facteur important, serais-je mieux tirer les attributs dehors dans une structure externe, par exemple .:

struct Vals 
{ 
    AVal a_val; 
    VBal b_val; 
}; 

stocker une Vals* dans chaque cas, et la réécriture Base comme suit?

class Base 
{ 
    //... 
    public: 
    AVal getAVal() const { return _vals->a_val; }; 
    BVal getBVal() const { return _vals->b_val; }; 
    //... 
    private: 
    Vals* _vals; 
}; 

La déréférence supplémentaire est-elle essentiellement la même que celle de vtable? Quel est l'idiome établi pour ce type de situation? Sont à la fois de ces solutions stupide? Tous les aperçus sont grandement appréciés

+2

Conception pour la clarté et l'évolutivité facile. Aucune méthode n'apparaîtra sur le radar d'un profileur à moins que ce soit votre opération de programme la plus interne. –

Répondre

1

La première méthode semble plus claire et vous oblige à remplacer ces méthodes (au premier enfant de toute façon). Je pense que les frais généraux des appels virtuels ont tendance à être inférieurs à ce que l'on pourrait attendre. Seulement si vous profilez le code et que les appels virtuels prennent beaucoup de temps, j'essaierais de faire des optimisations comme dans votre seconde approche. Cela dit, quel problème tentez-vous de résoudre? Parfois, les identifiants de classe comme celui-ci sont utiles, mais parfois une abstraction d'interface différente peut accomplir la même chose sans avoir une telle interface du tout.

+0

Eh bien, j'écris une machine virtuelle pour un langage de script que je suis en train de mettre ensemble. ** AVIS DE NON-RESPONSABILITÉ: ** * Ce projet est purement à des fins d'amusement/exploration/éducation. * La hiérarchie de classe réelle est l'ensemble des primitives de type script/classes de conteneur. Les attributs en cours d'accès sont constitués de diverses informations "type" (donc constante pour une classe, variable sur l'ensemble des classes). Parce que mélanger ces objets et exécuter ces getters est une grande partie de ce que fait le noyau de la VM, je voulais éviter de faire quelque chose qui était hoaky ou manifestement inefficace. – acanaday

1

Personnellement, j'implémenterais une sorte de, GetTypeStats(), qui renvoie une structure (référence à) qui contient toutes les informations spécifiques dérivées, ou une interface de requête comme dans D3D. Vous devriez également considérer le polymorphisme statique dans ce scénario. Cependant, si vos classes DOIVENT être polymorphes à l'exécution, alors vous ne pouvez rien faire pour éliminer les appels de fonction virtuelle.

+0

Oh oui, merci d'avoir soulevé cette question! L'implémentation actuelle utilise réellement la méthode de structure externe mentionnée ci-dessus et fournit une méthode getter pour obtenir directement l'accès const à la structure. – acanaday

+0

Vous pouvez créer la structure dans la classe de base et laisser la classe dérivée définir les membres de données dans le constructeur. Cela supprimerait les vfcalls. – Puppy

+0

Le problème avec cette approche (création de la structure dans la classe de base), si je ne me trompe pas, est que chaque instance contiendra alors une copie de la structure. En utilisant la méthode virtuelle ou le pointeur vers une structure externe, de nombreuses instances peuvent partager un ensemble d'informations de type. Ai-je mal compris? – acanaday

0

Si tout ce qui diffère sont les valeurs et ils sont fixés à la compilation, vous pouvez les faire arguments de modèle:

template< AVal aval, BVal bval> 
class Derived : public Base 
{ 
    AVal getAVal() const { return aval }; 
    BVal getBVal() const { return bval }; 
}; 

typedef Derived<A_VAL_ONE, B_VAL_ONE> One; 
typedef Derived<A_VAL_TWO, B_VAL_TWO> Two; 
+0

Ceci, cependant, ne contourne pas le besoin de virtuel et est moins lisible (IMHO) que les remplacements virtuels explicites dans le premier exemple. – acanaday

+0

@acanaday: Je suppose que le post de sbi, comme le mien, essayait juste de vous donner un aperçu supplémentaire. Ne sois pas haineux, nous essayons juste d'aider! :) –

+0

Oh, désolé! J'apprécie l'entrée de toute façon! – acanaday

0

C'est une façon courante de faire un pauvre homme de dynamic_cast pour les types polymorphes, lorsque le programmeur veut éviter l'utilisation de dynamic_cast. Dans ces cas, il s'agit d'une micro-optimisation. Comme avec toutes les micro-optimisations, vous devez utiliser un jugement basé sur besoin avant d'aller de l'avant avec elle. Si le profileur vous dit qu'il n'y a aucun gain de performance à faire ceci plutôt que dynamic_cast, vous seriez probablement beaucoup mieux en utilisant simplement dynamic_cast.

+0

Je ne suis pas en désaccord avec vous, mais je ne suis pas sûr de savoir comment 'dynamic_cast' est une * alternative * à ce que j'ai présenté ici. Je pourrais, par exemple, exécuter une liste de ces objets via une fonction dont le comportement change en fonction de la valeur renvoyée par l'une de ces fonctions. Suggérez-vous que j'essaie d'utiliser 'dynamic_cast' pour lancer les types dérivés dans la séquence jusqu'à ce que je frappe un cast qui ne retourne pas' NULL' au lieu d'activer la valeur getter? (Ai-je beaucoup mal compris votre suggestion?) – acanaday

+0

Je devrais aussi mentionner que déterminer la distribution appropriée de la base à la dérivée n'est pas nécessairement l'objectif ici. En fait, parfois, aucun casting n'est désiré. Suis-je encore malentendu? – acanaday

+0

@acanaday: Je ne dis pas que c'est le cas. Tout ce que je dis c'est que la plupart du temps, je vois une construction comme celle-ci, c'est une tentative de 'dynamic_cast' d'un pauvre. Mon message peut ou ne peut pas s'appliquer à vous. –