Design pour eviter un virtual... - C++ - Programmation
Marsh Posté le 06-09-2006 à 19:40:34
Il y a souvent des moyens d'éviter de dériver, qui est le moyen le moins souple d'ajouter des fonctionnalités à une classe.
La dérivation n'a réellement d'intérêt que si la relation "est un" est vérifiée et on n'est sûr que la hiérarchie de classes n'a aucun risque d'exploser combinatoirement.
Sinon, on peut utiliser la composition (pointeurs sur objets en tant que membres de classes), le pattern decorator pour rajouter des fonctionnalités, ou ajouter des propriétés grâce à une bibliothèque de mixins. Cette dernière méthode peut être élégante et performante si bien utilisée. (google est ton ami)
Marsh Posté le 06-09-2006 à 20:00:42
ReplyMarsh Posté le 07-09-2006 à 10:33:49
Joel F a écrit : ou faire de l'heritage statique avec des templates |
+1
Marsh Posté le 08-09-2006 à 02:03:34
N'auriez vous pas un exemple simple d'heritage statique avec template?
Merci d'avance!
Marsh Posté le 08-09-2006 à 14:08:44
Code :
|
Marsh Posté le 08-09-2006 à 15:21:02
Joel F a écrit : [cpp] |
t'auras beau essayé, s'il faut de la résolution dynamique, ben il la faut. C'est incompatible avec l'usage souhaité.
Quant à l'affirmation 'on sait que c'est lent' c'est typique de l'optimisation prématurée basée sur un mythe.
Marsh Posté le 08-09-2006 à 15:22:09
j'ai 2 questions:
quel est le sens de "heritage statique" ? pour moi c'a na aucun sens, l'heritage est un schema de structure qui est resolu a la compilation
a quoi sert ce modele ?
et puis ca ne reponds pas a la question qui est comment ecrire ce genre de code
Code :
|
sans l'inconvenient du cout d'un appel de methode virtuelle (mais qui est franchement negligeable pour peu que la methode fasse autre chose que 1+1, sortez vos profiler) et a ce probleme il n'y a pas de solution.
Marsh Posté le 08-09-2006 à 15:24:26
ReplyMarsh Posté le 08-09-2006 à 15:41:02
ReplyMarsh Posté le 08-09-2006 à 15:47:15
skelter a écrit : c'est quoi "l'heritage statique" ? |
Je pense qu'il vaut mieux parler de polymorphisme statique, plutôt que d'héritage statique.
Pour moi le polymorphisme statique est la possibilité pour une classe d'hériter du comportement d'une autre classe, sans pour autant perdre en performance, puisque tout se résout à la compilation grâce aux mécanismes de templates, plutôt qu'à l'exécution avec des vtables.
Cela dit, je suis d'accord avec Taz: cela ne remplace pas le dynamique s'il y en a vraiment besoin. Par contre il y a de nombreux cas où on use de l'héritage et du polymorphisme dynamique uniquement pour des besoins de design ; dans certains de ces cas, il pourrait être avantageux d'utiliser des templates.
La perte d'efficacité dues aux vtables n'est pas un mythe. Il suffit de faire quelques tests pour voir qu'un appel de fonction virtuelle coûte à peu près 5x plus cher qu'un appel normal. Donc il y a des cas dans lesquels il est utile de se passer des fonctions virtuelles. (il faudrait en savoir un peu plus sur le problème de zboub77 pour savoir si c'est le cas ici)
Marsh Posté le 08-09-2006 à 17:38:51
... c'est n'importe quoi ...
1.
/* ... code ... */
2.
Base * base;
3.
/* ... code avec test et pis ... */
4.
base = new A; /* ou base = new A; si je veux */
5.
base.do();
(je passe sur le do)
OK, un bon compilateur peut faire sans vtable là, mais étant donné un base*, le cas général, il doit passer par la vtable. S'il n'y a pas de résolution, bah il n'y a pas de surcharge possible. Je vois juste de l'enculage de mouche. Si tu mets pas virtual, ça résout rien, ça surcharge rien. "L'héritage statique" c'est de l'héritage sans virtual. Dans un contexte polymorphique ça ne marche pas. Circulez y a rien à voir.
Marsh Posté le 09-09-2006 à 18:11:30
yep, donc rien que je n'aurais pas vu...
En particuiler, la méthode "polymorphisme statique" ou "heritage statique" est appelée "Curiously Recursive Pattern". Comme dit Skelter, j'utilise ca pour faire quasiment un "1+1" dans le do()... et un très grand nombre de fois....
Marsh Posté le 09-09-2006 à 18:12:52
Vivement le mot clé "auto" dans la prochaine norme!! il y aura alors des astuces pour s'affranchir de ces problèmes!
En tous cas, merci!
Marsh Posté le 09-09-2006 à 23:27:44
Taz a écrit : ... c'est n'importe quoi ... |
Qu'est-ce qui est n'importe quoi ?
T'es pas d'accord avec ce que j'ai dit
Marsh Posté le 10-09-2006 à 00:51:31
Bon alors le polymorphisme dynamique (comprenez le bon vieux virtual) c'est le mal ou non ?
Marsh Posté le 10-09-2006 à 07:40:17
Citation : |
Non
J'avais posté il y a quelque temps un code de classe Polymère (c'était une blague) qui illustre le fonctionnement des vtables:
Code :
|
L'opérateur = de la class Polymère mère caste à l'arrache l'objet en char*. Mais comme la classe contient une fonction virtuelle, elle n'est pas organisée de la même façon. Le tout premier emplacement mémoire est occupé par un pointeur vers la table virtuelle:
|
Ce qui fait que l'opérateur =, au lieu d'affecter la chaîne comme laisserait penser son implémentation, change la vtable!!!
Du coup l'affichage de ce programme est le suivant:
|
Ceci parceque la vtable contient une liste de pointeurs vers les fonctions virtuelles de la classe:
|
Toutes les classes de même type contiennent un pointeur vers la même vtable. Ça se complique bien sûr pour les dérivations multiples.
Ok, donc pour récapituler, une fonction virtuelle doit passer par une vtable pour être appelée, et la vtable est une table de pointeurs sur fonction. Ce qui nous fait 3 sauts en mémoire au total avant d'arriver au code, et ce qui explique pourquoi on dit que les fonctions virtuelles sont plus lentes.
De plus les performances sur un PC se dégradent au niveau assembleur après le deuxième saut:
Appel de fonction: 1 saut, 1 instruction
Pointeur sur fonction: 2 sauts, 1 instruction
Fonction virtuelle: 3 sauts, 2 instructions
Mais celà ne veut nullement dire qu'on en a pas besoin et c'est même très pratique pour exploiter le polymorphisme, les multiples dérivations, etc... Il faut juste savoir de quoi on a besoin avant de coder.
Marsh Posté le 10-09-2006 à 13:33:02
OK merci pour la précision. Donc il faut choisir la solution appropriée à la problématique (privilégier l'évolutivité ou les perfs)
Marsh Posté le 10-09-2006 à 13:50:04
Celà ne va pas à l'encontre des perfs, les fonctions virtuelles sont tout simplement nécessaires dans certains cas, sans quoi il faudrait recréer leur fonctionnalité.
Marsh Posté le 10-09-2006 à 13:57:59
nargy a écrit : Celà ne va pas à l'encontre des perfs |
Pas clair. Quand tu parles du nombre de sauts nécessaires pour accéder à la fonction virtuelle référencée par le pointeur de la vtable, tu suggères donc que le nombre d'instructions nécessaires pour exécuter la procédure virtuelle est plus important que pour une procédure non virtuelle. Cela a donc bien un impact sur les performances.
Si l'élément de conception que tu mets en oeuvre n'a pas pour objet d'être polymorphique (au sens ou le modèle n'a pas pour vocation d'être étendu), tu tires donc avantage - en terme de performance - d'une solution ne mettant pas jeu la procédure virtuelle.
Ou est le défaut dans le raisonnement ?
Marsh Posté le 10-09-2006 à 14:39:33
Désolé, je n'ai pas dû bien comprendre:
> il faut choisir la solution appropriée à la problématique
> (privilégier l'évolutivité ou les perfs)
qui ne sont équivalents qu'à la condition que la problèmatique soit purement du domaine des perfs ou de l'évolutivité.
Si tu en as besoin, les fonctions virtuelles sont avec 1 instruction machine supplémentaire, la manière la plus efficace d'implémenter le polymorphisme.
Marsh Posté le 10-09-2006 à 17:25:31
nargy a écrit : Si tu en as besoin, les fonctions virtuelles sont avec 1 instruction machine supplémentaire, la manière la plus efficace d'implémenter le polymorphisme. |
Ouf on se comprend bien donc.
Marsh Posté le 10-09-2006 à 23:39:39
ReplyMarsh Posté le 10-09-2006 à 23:56:55
Citation : |
toujours aussi désagréable Taz.
Marsh Posté le 11-09-2006 à 09:05:27
Taz a écrit : bon je vais vomir dans mon coin devant tant d'immondices. |
Tu veux pas préciser un peu ce qui te gêne ?
Marsh Posté le 11-09-2006 à 14:29:35
Je pense que Taz veut dire que s'il n'y a pas de virtual, il n'y a pas d'heritage et donc pas de polymorphisme... Qu'en est il des données membres?
Marsh Posté le 12-09-2006 à 09:29:48
zboub77 a écrit : Je pense que Taz veut dire que s'il n'y a pas de virtual, il n'y a pas d'heritage et donc pas de polymorphisme... Qu'en est il des données membres? |
Tu peux avoir de l'héritage sans 'virtual'. C'est le polymorphisme dynamique que tu n'as pas sans 'virtual'
Marsh Posté le 13-09-2006 à 02:43:15
franceso a écrit : Tu peux avoir de l'héritage sans 'virtual'. C'est le polymorphisme dynamique que tu n'as pas sans 'virtual' |
Tu peux aussi avoir du polymorphisme dynamique sans virtual (et même sans héritage),
mais avec beaucoup de #define
Marsh Posté le 06-09-2006 à 18:51:54
Bonjour à tous,
J'ai un problème de design que je n'arrive pas à résoudre comme j'aimerais... Ceci se pose dans le cadre de recherche de performance pour des programmes à caractère scientifique.
Considerons les classes A et B héritées de Base, par exemple, en oubliant les constructeurs & co :
Je peux donc écrire
Ok mais on sait que la notion virtual handicape énormément en terme de performance si le do() a lieu dans un boucle par exemple. L'heritage doit rester haut niveau...
Auriez vous des idées pour garder une écriture générique tout en assurant des perfs pour un cas comme celui ci? Par exemple dans l'esprit des techniques curiously template?
Merci d'avance