Envoyé par
therwald
L'usage correct pour implémenter une fonction virtuelle dans des classes dérivées, c'est de définir CDerived::Allocate() dans CDerived, CSetType::Allocate() dans CSetType, et ainsi de suite. Par ailleurs, la définir pareil (retournant un CDerived) dans toutes les classes n'est pas à proprement parler incorrect, mais ça ne rime pas à grand chose...
Et encore!!!
Au cas où tu ne l'aurais pas remarqué, l'invocation de new se fait sous la fome de
pTemp = new CDerived (*this) ;
autrement dit, une référence, a priori constante (mais bon, comme allocate n'est pas une fonction constante dans le cas présent, on va faire main basse sur le sujet ) sur un objet dont le type peut, très bien etre:
- CBase ou CDerived dans CDerived ::Allocate()
- CBase ou CSetType dans CSetType::Allocate()
- CBase ou Cpoint dans Cpoint::Allocate()
- CBase ou CComplexe dans CComplexe::Allocate()
Si le constructeur invoqué prend la forme de CDerived(CBase /* const */ & b) afin d'instancier le membre correspondant.
Il n'y aura pas forcément de problème: on se trouve (plus ou moins) dans le cadre de l'utilisation du patron de conception "décorateur".
Mais, si c'est le type dérivé... aie ouille ouille aie aie
Cela implique que la classe CDerived devra avoir une connaissance de toutes les autres classes dérivées ca sent pour le moins mauvais en terme de conception.
Sinon, il reste une dernière solution:
Le fait que CSetType, Cpoint et CComplexe puissent dériver, non pas de CBase, mais de ... CDerived et que le constructeur prenne une référence (éventuellement constante) sur un objet de type CDerived afin d'instancier le membre correspondant.
Comme on se trouve alors dans un cadre fort proche du patron de conception "décorateur", on sera alors confronté au problème du fait que l'on en arrive (si la référence est constante) à autoriser la copie de l'objet, ce qui n'est pas forcément la meilleure des choses à faire
Mais, cette dernière solution n'est, visiblement, pas envisagée
Or, si le prof souhaite que toutes les fonctions allocate renvoient un pointeur pointant sur un nouvel objet de type CDerived tout en étant redéfinies spécifiquement pour chaque type dérivé, nous sommes bel et bien dans la deuxième solution (il faut un constructeur de CDerived prenant une référence sur chaque type dérivé).
Et alors là, comme je l'ai dit plus tot, il va y avoir des problèmes car, pour pouvoir travailler avec les types dérivés, il faut que le membre qui sera initialisé soit du type dérivé (autrement, il n'aurait servi à rien rendre ce comportement virtuel), soit une référence, soit un pointeur, soit une instance.
Sauf que voilà: une classe ayant sémantique d'entité ne devrait pas être copiable (en plus, cela signifie qu'il serait aussi possible de placer chaque type dérivé dans un état reconnu comme "invalide")... Adieu l'idée d'utiliser une instance, car, pour pouvoir la créer, il faudrait copier l'objet reçu en paramètre comme membre de la classe CDerived.
De plus, je me refuse (c'est une question de principe ) à initialiser un pointeur sur base de l'adresse d'une référence car on ne sais jamais jusqu'à quand l'objet qui a servi à la référence sera disponible... Adieu l'idée d'utiliser un pointeur comme membre de la classe CDerived.
Enfin, les références doivent être initialisée au moment de leur création... Adieux l'idée d'utiliser une référence comme membre de la classe CDerived.
Mais bon, continuons le raisonnement jusqu'au bout...
Peut être l'idée est elle d'initialiser l'objet de type CDerived avec des données issues des autres types dérivés
Mais, dans ce cas, il y a deux solutions:
Soit il existe un (voir des) comportement(s) commun(s) à tous les types dérivés (éventuellement redéfini(s) pour chaque type dérivé) qui permet(tent) de récupérer les valeurs qui nous intéressent, soit il faut utiliser un comportement spécifique à chaque type dérivé pour pouvoir le faire.
Dans le premier cas, Pourquoi ne pas déclarer ce comportement comme virtuel pur dans la classe de base
Seulement, voilà... Encore une fois, il n'est plus nécessaire de redéfinir cette fonction allocate dans tous les types dérivés: une fonction (non virutelle) déclarée (et définie) uniquement dans Base est amplement suffisante
Et, dans tous les cas, pourquoi se faire ch..er à transmettre une référence sur le type particulier et à nous "coltiner" une dépendance supplémentaire entre la classe CDerived et toutes les autres classes dérivées
Pourquoi ne pas créer un constructeur de CDerived qui prenne directement les valeurs (par référence éventuellement constante si besoin ) qui l'intéressent et faire en sorte que les fonction Allocate de chaque type dérivé fournissent directement ces valeurs
Bref:
@therwald je suis pour le moins mitigé quand tu dis que ce n'est pas à proprement parler "incorrect"... J'aurais tendance à dire que, bien que cela puisse être correct en terme de code, c'est totalement incorrect du point de vue conceptuel .
Et mon avis personnel est que les gens éviteraient énormément de problèmes s'ils prenaient l'habitude de réfléchir un tout petit peu en terme de conception avant de réfléchir en terme de code car rien ne pourra jamais faire qu'une mauvaise décision conceptuelle devienne valable "par la simple magie du code" .
(si tu veux que je précise ce point de vue, il vaudrait mieux ouvrir une nouvelle discussion, car je suppute par expérience que ca risque d'être un débat animé ).
@geocarre27 : Je vais taire ce que je pense réellement de ton prof, ca risquerait de le rendre nerveux.
Mais je te conseillerais bien volontiers de lui faire une réponse "diplomatique" du genre de "désolé monsieur, mais ce que vous me demandez ne rime à rien", quitte à réutiliser mes arguments voire, à lui donner le lien de cette discussion (raison pour laquelle je reste moi meme diplomate à son sujet )
De plus, même si ce n'est qu'un détail, je tiens quand même à préciser que j'ai déjà beaucoup de mal à envisager un quelconque point commun entre les classes CDerived, CSetType, Cpoint et CComplexe qui justifieraient qu'elles héritent toutes de CBase
Je suis réellement tout près à crier "attention!! tu te diriges tout droit vers un god object, et ce n'est vraiment pas l'idéal".
D'ailleurs, je le crie bien fort .
Ainsi, des données de type CPoint ou CComplexe auront, typiquement, sémantique de valeur, ce qui est fortement incompatible avec le fait de les intégrer dans une hiérarchie de classes.
J'aurais en outre tendance (d'après le nom ) à estimer que CSetType serait, typiquement, un genre de collection quelconque et, bien que les collections soient souvent "à mi chemin" entre une sémantique d'entité et une sémantique de valeur, il n'est pas meilleur de vouloir les introduire dans une hiérachie de classes.
Ne serait-ce que parce que les règles de la programmation par contrat font que l'on ne peut décemment pas estimer qu'une liste triée puisse hériter d'une liste (non triée)
Au final, la seule classe qui pourrait, éventuellement (et encore: uniquement parce que je n'en sais pas d'avantage pour l'instant ) hériter de CBase, ce serait... CDerivee
Partager