héritage multiple .. pour ou contre ? - C++ - Programmation
Marsh Posté le 06-04-2008 à 17:47:13
en général, tu peut t'en passer.
Perso, j'essaye de coller au modèle de java : 1 classe peut héritée d'une classe mère et de X interfaces. En C++, suffit de te dire que classe abstraie = interface
Marsh Posté le 06-04-2008 à 18:01:13
Joel F a écrit : en général, tu peut t'en passer. |
d'accord, merci. Alors en c++ il n'y a pas de différence entre interface et classe abstraite ? Et quand on parle d'"implémentation" ce sont les classes non abstraites ?
merci
Marsh Posté le 06-04-2008 à 18:10:47
C'est pas ça.
Y a pas de notion d'interface au sens propre en C++.
Juste que leur comportement corresponds à celle des classes abstraites
Marsh Posté le 06-04-2008 à 18:30:36
Joel F a écrit : C'est pas ça. |
ok, et pour ce qu'on appelle l'"implémentation" ?
Marsh Posté le 06-04-2008 à 19:16:01
et bien moi je suis pour, pour et encore pour.
D'ailleurs l'absence d'héritage multiple est un des reproches que je fais au langage D.
Déjà à propos des interfaces : ça implique de rendre les méthodes virtuelles alors que c'est rarement utile dans ce cas.
Oui je sais la perte de perf est faible tout ça, mais en C++ il y a un principe important : "Ne paye pas pour ce que tu n'utilise pas". Or imposer des appels virtuels pour contourner l'héritage multiple ne respecte pas ce principe.
Joel F parle du Java et je suis d'accord sur ce point, le Java fonctionne différement et est conçu pour faire des appels systématiquement virtuels (même si on peut les désactiver avec static, c'est rarement utilisé). Il est architecturé autour du principe des Interfaces, donc c'est correct de s'en serivir tant que possible.
Mais Java et C++ sont différents, et plus que ce que certains croient. J'utilise aussi certains paradigmes du Java qd je code en C++ (les accessors sans surcharge et les méthodes de conversions que je préfère aux opérateurs cast par exemple) mais uniquement qd ça respecte les principes de base du C++.
Quand tu veux faire une classe CamionPoubelle en C++, tu peux te contenter de dériver de Camion et de Poubelle. Pas besoin d'une interface ICamion et d'une IPoubelle remplie de méthodes virtuelles pures (appellée méthodes abstraites en java) que tu seras obligé de redéfinir même si t'en as pas besoin.
Je ne vois pas le problème à hériter de deux classes en même temps. Certes il y a des cas pourris comme l'héritage en losange (1) et l'héritatge virtuel mais j'ai beaucoup codé en C++ et je ne me souviens pas en avoir eu besoin. Dans des condisions réelles, les deux classes dont on héritent ne rentre jamais en conflit entre elles. Il faut le faire exprès pour avoir des méthodes qui ont le même nom par exemple.
Tant qu'on utilise pas de méthodes venus de certains design pattern comme Visit() ou serialize() il n'y a vraiment aucun problème.
Code :
|
Il est où le problème, elle est où la difficulté ?
En vrai l'héritage multiple c'est surtout chiant à gérer quand on écrit un compilo (les conversions de pointeurs provoquent des décalages...) mais pour le programmeur c'est très utile.
Pour répondre à la question de départ, il n'y a aucune fonctionnalité du C++ qui ne doit être éviter, sinon cette fonction ne serait pas dans le langage. C++ n'est pas un langage commercial comme le Java ou Delphi, donc qd il mette une fonctionnalité c'est qu'elle est utile, c'est pas juste pour faire plaisir à leurs actionnaires (2). Et en plus l'héritatge multiple n'entraine aucune perte de perf, contrairement au RTTI ou aux exceptions qu'on peut voiloir éviter dans une optique d'optimisation.
Et historiquement je me souviens qu'il y a qlq années on reprochait au C++ d'avoir les templates, soit disant ça servait à rien, c'était compliqué et Java s'en passait bien. Maintenant les templates sont de + en plus utilisés en C++ et le Java a fini par s'en dotter, sous une forme plus simple il est vrai (la généricité). Donc chercher à niveler vers le bas ça ne tient pas longtemps. On finit toujours par avoir besoin de fonctionnalités de + en + avancées.
(1) souvent mal traduit par "diamant" (venant de diamond en anglais, qui signifie diamant et losange)
(2) ouais c'est un gros troll contre Sun désolé, par contre pas contre Borland, on ne tire pas sur les ambulances
Marsh Posté le 06-04-2008 à 19:18:33
merde j'ai oublié les points-virgules derrière les def de classes
Marsh Posté le 06-04-2008 à 19:41:51
J'ai jamais prophétisé l'abolition de l'héritage multiple.
Moi, perso le camion poubelle, il hérite de camion car C'EST un camion et il implémente IPoubelle car il se COMPORTE COMME une poubelle.
après chacun sa sauce
Citation :
|
Dans certains cas de CRTP, ça arrive aussi :s
Marsh Posté le 06-04-2008 à 19:50:07
CRTP : skoi cet acronyme ?, ça me dit rien.
Avec l'héritage multiple du C++ tu n'as justement pas besoin de te demander si c' "est-une" poubelle ou si ça "implémente" (se comporte comme) une poubelle, puisque les deux reviennent au même niveau code.
Par contre dans un contexte Java, même avec l'héritage multiple possible, j'approuverais ton utilisation d'une interface pour Poubelle. Ca serait + élégant et pas plus couteux en perfs.
En C++ par convention le "is-a" est utilisé comme premier héritage, et le "implements" comme héritage secondaire. Ca permet aussi de pouvoir convertir en dur un pointeur de CamionPoubelle en Camion, mais pas en Poubelle. Mais c'est juste une convention.
Marsh Posté le 06-04-2008 à 19:57:29
jesus_christ a écrit : CRTP : skoi cet acronyme ?, ça me dit rien. |
Curiously Recursive Template Pattern
jesus_christ a écrit : |
Disons que j'aime bien faire ce distingo, ca aide à garder séparer les classes qui ont tendances à êtres concretes et les autres.
Encore une fois, ca n'engage que moi.
Marsh Posté le 06-04-2008 à 20:00:11
"Ca permet aussi de pouvoir convertir en dur un pointeur de CamionPoubelle en Camion, mais pas en Poubelle."
Je trouve ça un peu con
Moi qui croyait que justement l'héritage multiple permettait de faire ça...
Du coup je préfère les interfaces au moins on peut instancier un objet selon l'interface désirée, sans ce soucier de qui fait quoi à la base dans l'héritage.
A moins que j'aie mal compris la phrase ?
Marsh Posté le 06-04-2008 à 20:02:45
Curiously Recursive Template Pattern
Je connais ça, mais pas l'acronyme
Ca arriverait donc si les deux classes dont on hérite utilisent ce CRTP ? Oui en effet, c'est une forme d'héritage de noms en losange.
Marsh Posté le 06-04-2008 à 20:05:49
jesus_christ a écrit : |
ouais mais bon, pour en faire comme pas possible, ca a du m'arriver 2 fois quoi
Marsh Posté le 06-04-2008 à 20:09:14
qd je parle de "en dur" c'est du genre :
Code :
|
Pourquoi j'utilise un void* ?
Dans le cas d'une utilisation d'un langage externe (perso j'utilise souvent du LUA) pour lequel la donnée est un pointeur abstrait, le void* est inévitable. C'est pareil pour du code API Win32 :
Code :
|
edit : je précise qu'avec une Interface, ça ne marche pas mieux qd on utilise un void*, ça a même tendance à être pire.
Marsh Posté le 06-04-2008 à 23:28:06
Joel F a écrit : J'ai jamais prophétisé l'abolition de l'héritage multiple. |
En même temps, y'a des voitures qui sont des poubelles, mais c'est une autre histoire.
Marsh Posté le 07-04-2008 à 00:15:11
Salut,
merci beaucoup pour toutes ces explications très poussées .... Je viens de m'entrainer sur un exo d'héritage en losange, c'est mal ? (Je vais poster mon exo dans un autre thread, ce sera mieux)
@Jesus :
Code :
|
pourrais tu m'expliquer pourquoi Poubelle* p = (Poubelle*)ptr; ne marche pas ? est-ce par rapport au fait que dans l'héritage il y a d'abord "public Camion" et ensuite "public Poubelle" ?
merci encore
Marsh Posté le 07-04-2008 à 08:17:04
oui.
Dans CamionPoubelle, la partie Camion se trouve au début, donc l'adresse de la partie Camion de la structure est la même que celle de CamionPoubelle.
Par contre la partie Poubelle se trouve + loin, et si on prend l'adresse de la partie poubelle de la structure, il faut décaler le pointeur.
|
Marsh Posté le 07-04-2008 à 09:03:27
el muchacho a écrit : |
lol ^^ en effet
Marsh Posté le 07-04-2008 à 10:37:47
Bon au risque de me faire fusillé, j'ai développé un moteur de calcul qui utilise de l'héritage multiple et des losanges. Mes collègues bien plus forts que moi en C++ ne m'ont pas trop crié dessus, donc je vais expliquer quel était le problème de départ et ensuite si vous avez des idées pour faire mieux je suis évidemment preneur.
Mon problème est le suivant, je veux créer un produit qui a des caractéristique, et si c'est possible j'aimerai créer son type au moment de la compilation, on va dire que les caractéristiques sont Call,Barrière,Swap,Constant, etc... Ensuite j'ai plein de produit qui peuvent combiner une ou plusieurs de ses caractéristiques voire même plusieurs barrières, qui plus est une notion d'ordre est nécessaire parce que lorsque je dit "combiner" en fait je parle de composition (en terme de fonction). Ensuite les propriétés peuvent dériver entre elles, genre Putable dérive de Callable. Donc j'ai un beau merdier, et je suis hyper fainéant je veux éviter de réécrire plein de classes dérivées genre DoubleBarrière dérivant de Barrière. Le seul avantage c'est que mes propriétés partage le même squelette de base et ne comporte que très peu de méthodes. Donc j'ai pensé (out of the box) au type de structure suivant:
Toutes mes propriétes (que j'appelle module) dérivent d'une seule classe de base et comporte 2-3 méthodes, là je peux dériver entre elles et j'ai toute une famille à ma disposition. En plus je les définit avec un argument template <int> dummy pour pouvoir combiner par la suite les même modules (je sais c'est pas beaux mais ca a aussi des côtés intéressants).
Enfin je décide de créer une class qui hérite virtuellement de deux modules pour les mélanger, là je décrit les relations d'ordres (qui on va créer en premier), et la composition fonctionelle, la classe ressemble à ca:
Code :
|
Bon ensuite pour donner un exemple lorsque je veux créer un produit qui correspond à une Bairrière sur un Call je fait:
Code :
|
Lorsque je veux créer deux barrières sur un call:
Code :
|
etc... J'ai une récurrence vachement flexible pour prototyper des produits, j'aime bien c'est cool, m'enfin ca reste du losange... Sinon lorsque j'ai une famille de propriétés qui vont partager des variables similaires et bien je peux les définir statique par famille, genre une famille de Callable 0 et 1 i.e. Callable<0> et Callable<1>. Jusqu'à présent l'expérience nous montre que malgré un design alambiqué le prototypage de produit est devenu très rapide (je suis dans un environnement ou quelques heures peuvent faire la différence), et c'est assez efficaces en terme de temps de calcul. Ensuite c'est une solution à mon problème particulier, et je suis sûr que l'on peut faire mieux (d'ailleurs j'attends les suggestions).
Marsh Posté le 07-04-2008 à 10:47:05
Encore une fois : j'ai rien contre l'héritage multiple, juste que si je peut m'en passer, je m'en passe. ^^
@ElDesdichado : ton truc c'est purement 'industriel' ou y a des papiers dessus ? Je cherche des exemples de ce types d'idiomes de prog hors HPC.
Marsh Posté le 07-04-2008 à 10:54:32
Non purement industriel (enfin adaptés à nos besoin). M'enfin je dirai plutôt purement amateur, dans la mesure où c'est de bibi i.e. avec mes mimines et mes idées foireuses, donc si il y a des articles je suis preneur. Pour ce qui est du code en entier, je peux pas vraiment le poster, ils me versent un salaire donc... Quant à savoir si c'est bien fait ou d'un quelconque intérêt je précise que je suis mathématicien et pas un expert du C++ (heureusement j'ai un collègues qui s'y connaît pas mal). Quoiqu'il en soit on l'utilise en production pour chiffrer autre chose que des boîtes à savon
Marsh Posté le 07-04-2008 à 11:09:26
c'était exactement ce genre d'infos que je voulais (les boites à savons )
Marsh Posté le 07-04-2008 à 11:18:52
M'enfin il était pas difficile de deviner de quelles boîtes à savon je parle. De toute manière je t'ai PM.
Marsh Posté le 07-04-2008 à 13:48:10
jesus_christ a écrit : oui.
|
d'accord ! c'est vrai que c'est logique en fait!
merci
Marsh Posté le 23-04-2008 à 16:19:26
in_your_phion a écrit :
|
up,
j'ai essayé ça et ça marche dans les trois cas ... kesako ??
Marsh Posté le 23-04-2008 à 20:09:57
t'es bien passé par un void* ?
Une de tes deux classes de base n'est pas vide ?
T'as essayé de compiler, ou vraiment d'éxécuter ?
Parce que avec 2 classes non vides et en passant par un voird*, c'est juste pas possible que ça marche !
Marsh Posté le 24-04-2008 à 00:46:46
de toutes façons utiliser le cast C (Type*) en C++, c'est mal
et sinon l'héritage multiple c'est très pratique et ça permet de bien mieux factoriser le code que l'interfaçage à la java
Marsh Posté le 24-04-2008 à 14:25:06
jesus_christ a écrit : t'es bien passé par un void* ? Parce que avec 2 classes non vides et en passant par un voird*, c'est juste pas possible que ça marche ! |
salut,
j'ai essayé ça mais peut être que c'est pas bon alors comme ça marche :
Code :
|
Marsh Posté le 24-04-2008 à 16:06:24
Joel F a écrit : ou pas ! |
pour le premier je me sens obligé d'insister, en plus d'être illisible, ça masque totalement le type de cast effectué
pour le 2e, y a la composition pour contrebalancer, mais c'est chiant
Marsh Posté le 24-04-2008 à 16:34:00
pour le C-style cast, je t'accorde que un bon vieux xxx_cast<> a plus de charme. Malheureusement, dés fois, ca fait trop ce que tu veut
Marsh Posté le 24-04-2008 à 23:58:34
Joel F a écrit : |
D'ailleurs ne dit-on pas parfois : "fait chier, j'ai encore hérité d'une poubelle " ?
Nouvel exemple d'héritage dont on voudrait bien se passer.
Marsh Posté le 25-04-2008 à 21:15:50
Code :
|
Marsh Posté le 25-04-2008 à 21:18:05
system("pause" ); <- pas bien, pas portable et si tu veux arréter ton prog à la fin, mets un breakpoint ou passe par un .bat/.sh
ici avec un peu de malchance ça va pas cracher car les deux classes sont de taille vide sauf pour le vtable-pointeur. Donc si ça se trouve ça va pas planter et afficher "A says hello".
Marsh Posté le 25-04-2008 à 22:01:16
jesus_christ a écrit : system("pause" ); <- pas bien, pas portable et si tu veux arréter ton prog à la fin, mets un breakpoint ou passe par un .bat/.sh |
cin.get() ? :whistle:
</contribution à 2 balles>
Marsh Posté le 25-04-2008 à 22:06:29
getchar() ou cin.get à la rigueur...
mais bon débugger avec des getchar() et des printf()... il y a de très bons debugger gratuits en plus.
Marsh Posté le 06-04-2008 à 17:32:38
Bonjour,
J'aimerai savoir ce que vous pensez de l'héritage multiple en c++, êtes vous plutôt pour ou contre ? J'ai l'impression que beaucoup de langages oo ne le permettent pas ou plus, alors je me demande si c'est quelque chose qui doit être pratiqué ou évité en c++ ?
merci d'avance
Message édité par in_your_phion le 06-04-2008 à 17:33:00