IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Héritage sans virtualité ?


Sujet :

Langage C++

  1. #21
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 372
    Points : 23 628
    Points
    23 628
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Le polymorphisme, qui est une chose qui n'est permise que grâce au mécanisme d'héritage, est donc indissociable du mécanisme en lui-même, ne serait ce que pour le comportement de "destructibilité"
    Je crois que c'est là que nos opinions diffèrent : si le polymorphisme implique l'héritage (et encore, ça dépend de ce dont on parle), ce n'est pas réciproque pour autant.

    Je comprends parfaitement ta vision des choses et le cas d'une collection d'instances de différentes classes d'une même famille est effectivement un cas extrêmement courant en POO, par exemple lorsque l'on fait la liste des widgets d'une fenêtre d'une interface graphique. Par contre, je ne considère pas ce modèle comme l'axiome fondamental de la programmation orientée objet mais seulement comme l'un de ses cas d'utilisation.

    C'est également l'une des nombreuses choses qui me font apprécier le C++ plus que beaucoup d'autres langages orientés objets. Pour moi, cela va bien au delà du simple principe de « consommateur payeur ». Il s'agit réellement de travailler le plus possible sur le plan sémantique, d'une part, et de ne s'appuyer sur des entités externes au modèle qu'en dernier recours, spécialement si elles reposent sur des ressources qui ne sont disponibles qu'au runtime, d'autre part.

    ... Et tu perds une partie des informations de type FicheEtendue, ce qui est inacceptable
    Non, pas si le conteneur est vraiment ce qu'il prétend être, c'est-à-dire une collection de « Fiches » et pas une interface d'accès à des objets de différents types. Et c'est bien là la seule chose requise par le LSP.

    Le cas de la destruction est particulier : d'abord parce sémantiquement, on ne demande jamais à un objet de s'auto-détruire : ça se fait soit implicitement en fin de bloc, soit via l'opérateur delete et dans ce dernier cas, l'opérateur est statique, même s'il peut être défini au sein de la classe.

    Ensuite, parce que ça implique au moins un niveau d'indirection et de passer par des pointeurs explicites, non seulement pour pouvoir référencer de manière uniforme des objets de taille différentes et inconnues à l'avance, mais également pour pouvoir les détruire, avec delete donc. Si tu adoptes le principe du « zéro pointeur tout référence » souvent préconisé en C++ même s'il est très difficile à respecter totalement, tu ne peux pas rendre ce service, sauf à retrouver l'adresse de l'objet référencé avec « &xxx », à supposer qu'il a effectivement été instancié dans le tas avec new et à rendre la référence invalide passé ton delete et jusqu'à la fin du bloc ou elle est définie.

    Mais si tu te base sur le contenu de ton conteneur pour les détruire (par exemple, au moment du grand "clean up" avant de quitter l'application), ce sera ce qui est pointé par des... pointeurs sur le type de base que tu essayeras de détruire, et, si le destructeur est public et non virtuel, seuls les membres correspondant à la classe de base seront correctement détruits
    Oui, mais comme dit plus haut, si tu ne te bases pas sur ce contenu pour les détruire mais que tu laisses ce soin aux mécanismes qui ont instancié les objets dérivés, ça reste quand même de l'héritage.

    C'est ce que j'essayais de dire avec ma lib externe : si tu utilises un objet A défini dans une bibliothèque que tu n'as pas écrite et dont ni les fonctions-membres ordinaires ni les constructeurs n'ont été virtualisés, tu peux quand même étendre A vers B et te baser sur ce nouveau type dans tes propres programmes sans avoir à tout réécrire. Tu peux même passer tes objets aux fonctions de la bibliothèque originale, tant que celle-ci ne réclame pas leur destruction.

    J'en suis conscient, mais la destruction d'un objet se doit, malheureusement, s'adapter au type réel de l'objet. Ce n'est pas parce que tu ne crées pas explicitement un destructeur qu'il n'y en a pas, bien au contraire: Le compilateur en rajoute d'office un qui est public et non virtuel Tout comme le compilateur rajoute un constructeur par défaut, un constructeur par copie et un opérateur d'affectation si l'on ne prend pas les dispositions pour l'en empêcher C'est, justement, pour prendre cette particularité en compte que la notion de POD a été revue dans la norme
    Ce qui me conduit à la conclusion qu'en fin de compte, il n'y a vraiment que le cas de la destruction d'objet qui puisse lier LSP, héritage et virtualité.

    Ça vaut le coup de s'y attarder : d'un côté, je ne peux pas passer un « B * » à un delete conçu pour « A * » si mon destructeur n'est pas virtuel sans fuite de mémoire dans des conditions courantes d'utilisation. Donc, non substituabilité : LSP par terre.

    De l'autre côté : peut-on réellement considérer la destruction d'un objet comme une de ses propriétés ? C'est ce que je sous-entendais dans mes précédents paragraphes. Sur le plan sémantique et syntaxique, c'est une action extérieure et il n'y a plus de sens à vérifier si une propriété de l'objet est vraie ou fausse si l'objet n'existe plus.

    En outre, lorsque le destructeur d'un objet A n'est pas virtuel, il s'engage à faire le ménage nécessaire dans sa propre classe mais en aucun à garantir celui des classes dérivées. Ça n'a jamais fait partie de son contrat. De fait, un delete sur un pointeur « A * » qui pointe en fait un B, même s'il laisse dans le vide les ressources utilisées par B (ce qui, bien sûr, n'est pas souhaitable), remplit toutes les conditions connues et attendues à la compilation à l'endroit où le delete est écrit. De ce côté là, le LSP est toujours debout.

    En outre, il s'agit d'une nécessité due à la façon dont le C++ gère ses objets, mais ce n'est pas une caractéristique universelle de tous les systèmes informatiques proposant un mécanisme d'héritage.

    Ces trois derniers points me laissent penser que le cas de la destruction d'un objet est probablement en dehors du périmètre du LSP. Et si ce n'est pas le cas, alors il s'agit d'une exception.

    Mais, justement, il te faut un facteur discriminant permettant d'identifier de manière unique et non ambigüe une fiche particulière parmis les différents homonymes qu'il peut y avoir...

    Si, voulant modifier la fiche de Gerard Lambert de Calais, boucher de son état, tu en viens à modifier la ficher de Gerard Lambert de Paris, Carrossier de son état, il y aura un problème...

    Nous sommes donc bel et bien face à la définition d'une classe ayant sémantique d'entité : deux instances de la classes peuvent parfaitement avoir des attributs strictement identiques mais représenter des entités diffférentes, et donc seront considérées différentes si l'adresse mémoire à laquelle elles se trouvent sont différentes
    Avec le destructeur public et non virtuel, j'ai déjà démontré que le comportement de destruction n'est pas correct dans certaines situations.

    Tu pourrais, d'un autre coté, éviter la virtualité si le destructeur de Fiche était protégé, mais

    Si le destructeur de Fiche est protétgé, le compilateur t'envoie une erreur sur un code aussi simple que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void foo()
    {
        Fiche mafiche;
        /* ... */
    } //Erreur ici : tentative d'utiliser le destructeur de mafiche qui est protégé dans ce contexte
    ce qui fait que tu ne peux pas créer d'instance de Fiche, vu que tu ne peux pas la détruire.

    Si ce n'est pas mettre une restriction forte à l'utilisation de Fiche, dis moi ce que c'est
    Certes, mais rien t'empêche soit d'ajouter un numéro de séquence unique en utilisant une variable static à la classe mère, soit d'utiliser les pointeurs vers les instances respectives pour voir s'il s'agit ou non de la même fiche, soit encore de faire les deux. Dans tous ces cas, aucun recours à une quelconque fonction virtuelle n'est requise. Sauf, encore une fois, dans le cas de la destruction.

    À moins que tu te réfères au fait de stocker des copies de la classe de base dans le container. Là, bien sûr, ça crée des individus supplémentaires qui ne sont pas souhaitables si tu te places dans la sémantique d'entité. Mais là, tu peux t'en tenir aux pointeurs ou aux références et à vrai dire, ça ne t'oblige même pas à les dériver.

    Si, en revanche, on considère que l'information est donnée par la nature même de l'instance, donc au type dérivé auquel elle appartient, c'est une information qu'on ne peut pas obtenir non plus, sauf à faire du RTTI ou à implémenter une méthode maison pour s'identifier. Mais dans ce cas, ça ne concerne pas le LSP ni l'héritage en général. Par ailleurs, quand on en est là, il vaut mieux changer de repère et passer à un modèle « tout interprété », ce qui aura le mérite d'apporter tout ce qui va avec, comme l'introspection.

    D'ailleurs, le paradigme qui repose sur une couche interprétée, où tout fonctionne par transmission de référence et où toutes les fonctions sont virtuelles par défaut est celui adopté par Java, de façon totalement avouée.

  2. #22
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    J'ai pas encore pris le temps de tout lire, mais pour rappel utiliser delete sur un objet de type fille via un pointeur de type mère avec un destructeur non virtuel n'est pas une fuite mémoire, mais un UB. Meme si souvent c'est une fuite mémoire, se baser sur le fonctionnement réel des compilateurs dans une discussion sur la "logique" du langage me semble étrange.

  3. #23
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Je crois que c'est là que nos opinions diffèrent : si le polymorphisme implique l'héritage (et encore, ça dépend de ce dont on parle), ce n'est pas réciproque pour autant.
    Où ai-je dit que c'était réciproque j'ai juste dit que le polymorphisme est indissociable de l'héritage, et j'ai justifié cette phrase parle fait que la "destructibilité" de l'objet est un comportement qui doit pouvoir s'adapter au type réellement utilisé quand celui-ci passe pour etre du type de la classe de base.
    Non, pas si le conteneur est vraiment ce qu'il prétend être, c'est-à-dire une collection de « Fiches » et pas une interface d'accès à des objets de différents types. Et c'est bien là la seule chose requise par le LSP.
    Mais, si c'est un conteneur de fiche (sous entendu, ou est ce que je me trompe sous forme de valeur), tu perds une partie d'information si tu essaye d'y introduire une (copie de) FicheEtendue, et c'est inacceptable.

    Le cas de la destruction est particulier : d'abord parce sémantiquement, on ne demande jamais à un objet de s'auto-détruire : ça se fait soit implicitement en fin de bloc, soit via l'opérateur delete et dans ce dernier cas, l'opérateur est statique, même s'il peut être défini au sein de la classe.
    On ne lui demande pas de s'auto-détruire, mais l'un des services qu'il doit rendre est d'être correctement destructible, nuance

    Ensuite, parce que ça implique au moins un niveau d'indirection et de passer par des pointeurs explicites, non seulement pour pouvoir référencer de manière uniforme des objets de taille différentes et inconnues à l'avance, mais également pour pouvoir les détruire, avec delete donc. Si tu adoptes le principe du « zéro pointeur tout référence » souvent préconisé en C++ même s'il est très difficile à respecter totalement, tu ne peux pas rendre ce service, sauf à retrouver l'adresse de l'objet référencé avec « &xxx », à supposer qu'il a effectivement été instancié dans le tas avec new et à rendre la référence invalide passé ton delete et jusqu'à la fin du bloc ou elle est définie.
    J'applique plus volontiers le principe de "la référence chaque fois que possible, le pointeur quand on n'a pas le choix", c'est plus souple comme principe
    Oui, mais comme dit plus haut, si tu ne te bases pas sur ce contenu pour les détruire mais que tu laisses ce soin aux mécanismes qui ont instancié les objets dérivés, ça reste quand même de l'héritage.
    Mais il y aura, de toutes façons un mécanisme de destruction à prévoir "quelque part", surout que, malgré tout, ta classe a sensiblement sémantique d'entité, vu qu'il faut un facteur discriminant

    C'est ce que j'essayais de dire avec ma lib externe : si tu utilises un objet A défini dans une bibliothèque que tu n'as pas écrite et dont ni les fonctions-membres ordinaires ni les constructeurs n'ont été virtualisés, tu peux quand même étendre A vers B et te baser sur ce nouveau type dans tes propres programmes sans avoir à tout réécrire. Tu peux même passer tes objets aux fonctions de la bibliothèque originale, tant que celle-ci ne réclame pas leur destruction.
    Non, a priori, si une lib externe fournit une classe sans destructeur virtuel public (ou sans destructeur protégé et non virtuel), c'est que la classe en question n'a pas vocation à être dérivée, et tout ce que tu peux envisager, c'est l'agrégation, quitte à "déporter" les fonctions qui t'intéressent dans cette classe dans l'interface de la tienne
    Ce qui me conduit à la conclusion qu'en fin de compte, il n'y a vraiment que le cas de la destruction d'objet qui puisse lier LSP, héritage et virtualité.
    Il n'y a peut etre que cela, mais c'est déjà énorme, vu qu'il s'agit d'un comportement tout aussi indispensable que celui qui consiste à respirer pour l'homme
    Ça vaut le coup de s'y attarder : d'un côté, je ne peux pas passer un « B * » à un delete conçu pour « A * » si mon destructeur n'est pas virtuel sans fuite de mémoire dans des conditions courantes d'utilisation. Donc, non substituabilité : LSP par terre.
    N'inversons pas les rôles ni les étapes...

    Ce n'est pas LSP qui doit se plier à ce que tu veux, mais c'est bien toi qui doit veiller à le respecter

    En outre, comme je l'ai maintenu tout au long d'une discussion entrée dans les annales, LSP est un principe de conception qui doit, en tant que tel, intervenir très tot dans le processus et dont le respect est une condition sine qua non à toute possibilité d'héritage...
    De l'autre côté : peut-on réellement considérer la destruction d'un objet comme une de ses propriétés ? C'est ce que je sous-entendais dans mes précédents paragraphes. Sur le plan sémantique et syntaxique, c'est une action extérieure et il n'y a plus de sens à vérifier si une propriété de l'objet est vraie ou fausse si l'objet n'existe plus.
    La destruction est une action extérieure, mais la "destructibilité" (comprends : le comportement permettant à l'objet de faire correctement le ménage de tous les membres qu'il utilise) fait, bien évidemment, partie de ses propriétés : cf les formes canoniques de coplien
    En outre, lorsque le destructeur d'un objet A n'est pas virtuel, il
    s'engage à faire le ménage nécessaire dans sa propre classe mais en aucun à garantir celui des classes dérivées.
    Une classe de base n'a, de toutes façons, jamais à prendre le moindre engagement vis à vis des classes dérivées...

    Mais cela ne veut pas dire qu'elle ne doit pas accepter que l'une des classes dérivées puisse adapter l'un de ses comportement en fonction du type réellement utilisé
    Ça n'a jamais fait partie de son contrat.
    On est bien d'accord là dessus
    De fait, un delete sur un pointeur « A * » qui pointe en fait un B, même s'il laisse dans le vide les ressources utilisées par B (ce qui, bien sûr, n'est pas souhaitable), remplit toutes les conditions connues et attendues à la compilation à l'endroit où le delete est écrit. De ce côté là, le LSP est toujours debout.
    Non, si tu as un destructeur public et non virtuel dans la classe de base et que tu appelle delete sur un pointeur de la classe de base, tu as un comportement indéterminé...

    C'est peut etre la pire chose qui puisse t'arriver lorsque tu fais de la programmation
    En outre, il s'agit d'une nécessité due à la façon dont le C++ gère ses objets, mais ce n'est pas une caractéristique universelle de tous les systèmes informatiques proposant un mécanisme d'héritage.
    C'est surtout parce que C++ est le seul langage récent proposant l'héritage à ne pas rendre les fonctions virtuelles par défaut (et à donner éventuellement un mécanisme "terminal" ou similaire indiquant qu'une fonction n'a plus vocation à être redéfinie)...

    Mais la virtualité des fonctions, ou tout mécanisme pouvant s'en rapprocher, et le polymorphisme en général nécessite qu'une fonction déclarée dans la classe de base puisse éventuellement pouvoir adapter son comportement au type réellement manipuler quand on a affaire à un objet "passant pour etre" du type de la classe de base.

    Du moins, c'est le cas de tous les langages récents (car, ne connaissant réellement ni Ada ni SmallTalk, je n'ose pas m'exprimer à leur sujet )
    Ces trois derniers points me laissent penser que le cas de la destruction d'un objet est probablement en dehors du périmètre du LSP. Et si ce n'est pas le cas, alors il s'agit d'une exception.
    Il n'est pas du tout en dehors du périmètre de LSP, à partir du moment où il fait partie de l'interface publique (et qu'il peut donc être considérée comme étant une propriété valide de la classe de base) et, non, il ne s'agit pas d'une exception car toute fonction faisant partie de l'interface publique de la classe de base est à considérer comme étant propriété valide de la classe de base, et se doit donc d'être également valide pour les sous types.

    Maintenant, il y a, effectivement, des comportements qui ne nécessitent absolument aucune adaptation au niveau des sous types et que l'on peut donc laisser non virtuels (si tant est que l'on donne un comportement cohérent à la classe de base).

    Mais, pour que les comportements qui n'entrent pas dans la catégorie que je viens de citer puissent être considérés comme étant "propriété valide" du sous type, il faudra (oserais-je dire "d'office") permettre qu'ils s'adaptent en fonction du type dynamique de l'objet manipulé.
    Certes, mais rien t'empêche soit d'ajouter un numéro de séquence unique en utilisant une variable static à la classe mère, soit d'utiliser les pointeurs vers les instances respectives pour voir s'il s'agit ou non de la même fiche, soit encore de faire les deux. Dans tous ces cas, aucun recours à une quelconque fonction virtuelle n'est requise. Sauf, encore une fois, dans le cas de la destruction.
    Le problème, c'est que même s'il n'y a "que" la destruction qui doit s'adapter (parce que tu déciderais volontairement de fouler au pied le principe d'encapsulation / de ségrégation de l'interface), étant donné que c'est un comportement majeur et incontournable que doit offrir tout type que tu peux créer, tu n'as pas vraiment le choix, dés le moment où tu envisages qu'une classe puisse servir de base à des sous types
    À moins que tu te réfères au fait de stocker des copies de la classe de base dans le container. Là, bien sûr, ça crée des individus supplémentaires qui ne sont pas souhaitables si tu te places dans la sémantique d'entité. Mais là, tu peux t'en tenir aux pointeurs ou aux références et à vrai dire, ça ne t'oblige même pas à les dériver.
    Comme je l'ai dit, stocker dans un conteneur des Fiche par valeur est inacceptable si tu décide de travailler avec des FicheEtendue, du fait de la perte d'information que cela entraine
    Si, en revanche, on considère que l'information est donnée par la nature même de l'instance, donc au type dérivé auquel elle appartient, c'est une information qu'on ne peut pas obtenir non plus, sauf à faire du RTTI ou à implémenter une méthode maison pour s'identifier. Mais dans ce cas, ça ne concerne pas le LSP ni l'héritage en général. Par ailleurs, quand on en est là, il vaut mieux changer de repère et passer à un modèle « tout interprété », ce qui aura le mérite d'apporter tout ce qui va avec, comme l'introspection.
    C'est pour cela que tu as des mécanismes comme l'encapsulation et le fait de penser en terme de services rendus, pouvant éventuellement s'adapter au type réel de l'objet manipulé...
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  4. #24
    Invité
    Invité(e)
    Par défaut
    bonjour,

    j'ai juste dit que le polymorphisme est indissociable de l'héritage
    Indissociable, c'est assez ambigu, en francais ca se comprend comme polymorphisme ne peut exister sans l'heritage.
    En logique, indissociable a plus sens de symetrisme (donc implication double sens)


    en fait, (koala01 et Obsidian) vous vous comprenez pas je pense.
    Il me semble qu'Obsidian aborde l'héritage sans virtualité comme une possibilité. Ce qui est le cas. Il n'y a aucun probleme à laisser des string ou autres dans ficheEtendue du moment qu'on déréférence pas ficheEtendue en fiche (pour la destruction). C'est limité, tout ce qu'on veut, mais le usecase est possible.

    Alors que koala01 voit l'héritage dans son ensemble: à savoir, si on enlève la virtualité de l'héritage, alors on va se retrouver avec des comportements non désirables. En particulier avec le déréférencement sur type de base. Et il faut donc la virtualité pour pallier ces problèmes.

    Donc il faut être clair.
    Non, on est pas obligé d'utiliser la virtualisation pour faire de l'héritage,
    mais oui l'héritage a besoin de la virtualisation :
    - du côté technique, si on désire le polymorphisme
    - du côté fonctionnel, si on veut redéfinir des services qui s'appèlent pareil

  5. #25
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par koala01 Voir le message
    le polymorphisme est indissociable de l'héritage
    Je ne suis pas d’accord : l’héritage est un moyen de faire du polymorphisme. Pas le seul (cf Duck typing).

    Citation Envoyé par koala01 Voir le message
    En outre, comme je l'ai maintenu tout au long d'une discussion entrée dans les annales, LSP est un principe de conception qui doit, en tant que tel, intervenir très tot dans le processus et dont le respect est une condition sine qua non à toute possibilité d'héritage...
    Ton raisonnement se mort la queue. Tu n’envisages pas un héritage qui ne respecterait pas le LSP, du coup, tu te places dans un contexte où tu es effectivement obligé d’avoir les deux. C’est cohérent, mais je pense que ce faisant tu te prives de possibilités d’optimisations (dont tu n’as pas besoin dans 95% des cas), et de possibilités de conception intéressantes.

    Et juste pour en remettre une couche, LSP se contrefout de l’héritage : il s’applique tout aussi bien au duck typing. Il ne traite que de polymorphisme.

  6. #26
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Et juste pour en remettre une couche, LSP se contrefout de l’héritage : il s’applique tout aussi bien au duck typing. Il ne traite que de polymorphisme.
    non, LSP traite d'abord et avant tout de la subsituabilité et du sous typage.

    On est donc beaucoup plus proche du concept d'héritage que du concept de polymorphisme, vu que l'héritage est le moyen technique permettant de "concrétiser" les notions "d'objets substituables" et de "sous types"

    Le fait qu'une fonction soit polymorphe, ce n'est, d'une certaine manière, qu'une "facilité convenue" permettant dans certains cas d'estimer que la propriété associée à cette fonction puisse être considérée malgré tout comme valide parce que son comportement s'adapte au type réel.

    Si je dis que le polymorphisme est indissociable de LSP, c'est essentiellement parce que, parmis toutes les propriété que peuvent avoir n'importe quel objet, il y en a qui sont incontournables et que, parmis les propriétés incontournables, il y en a une (la "destructibilité") qui se doit de voir son comportement adapté au type réel .
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #27
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par koala01 Voir le message
    non, LSP traite d'abord et avant tout de la subsituabilité et du sous typage.
    Enlève sous-typage, c’est de trop dans le cas du duck typing.

    parmis toutes les propriété que peuvent avoir n'importe quel objet, il y en a qui sont incontournables et que, parmis les propriétés incontournables, il y en a une (la "destructibilité") qui se doit de voir son comportement adapté au type réel .
    C’est vachement discutable, ça. Détruire l’objet ne fait pas forcément partie des propriétés qu’on expose. Ça a par exemple toutes ses raisons d’être dans une librairie, qui fournit l’accès à des objets dont elle gère elle-même la responsabilité, et donc ne fournit pas au client un moyen de les détruire.

  8. #28
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Je ne connaissais pas le terme de duck typing (bien que je l'ai souvent utilisé), mais je ne vois pas ça comme une sorte d'héritage : il n'y a aucune relation entre les items potentiellement utilisables. Ils doivent juste posséder un fragment d'interface semblable (et non commune).

    D'ailleurs le moyen de faire du duck typing en C++ sera via les template, et non via l'héritage.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  9. #29
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Je ne suis pas d’accord : l’héritage est un moyen de faire du polymorphisme. Pas le seul (cf Duck typing).



    Ton raisonnement se mort la queue. Tu n’envisages pas un héritage qui ne respecterait pas le LSP, du coup, tu te places dans un contexte où tu es effectivement obligé d’avoir les deux. C’est cohérent, mais je pense que ce faisant tu te prives de possibilités d’optimisations (dont tu n’as pas besoin dans 95% des cas), et de possibilités de conception intéressantes.

    Et juste pour en remettre une couche, LSP se contrefout de l’héritage : il s’applique tout aussi bien au duck typing. Il ne traite que de polymorphisme.

    Personnellement je suis entièrement d'accord avec Koala01 (en tout cas dans les parties que je comprends). Mais je ne penses pas que vous soyez vraiment sur le même fil de discussion, Koala a une vision conceptuelle idéale de ce que l'on doit faire, vous défendez une vision pratique de ce que l'on peut faire.

    Pour LSP, il est clair que tu as tort. LSP parle avant tout de la notion d'héritage, ça saute aux yeux.

    Sinon c'est pas pour ça que je me permets d'écrire. Malgré nos avis divergents je suis intéressé par ce que tu as dis, et j'aimerais avoir un exemple de "possibilités de conception intéressantes" ne respectant pas le LSP (vraiment par curiosité hein, je n'ironise pas)
    Nullius in verba

  10. #30
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Kaamui Voir le message
    Personnellement je suis entièrement d'accord avec Koala01 (en tout cas dans les parties que je comprends). Mais je ne penses pas que vous soyez vraiment sur le même fil de discussion, Koala a une vision conceptuelle idéale de ce que l'on doit faire, vous défendez une vision pratique de ce que l'on peut faire.
    Le problème d'une vision pratique de ce que l'on peut faire (comprends :de ce que le langage permet par défaut, car n'étant pas armé pour vérifier que tu tu as pris une décision "conceptuellement correcte"), c'est que C++ étant particulièrement permissif, il t'autorisera de faire certaines choses, conceptuellement fausses, qui, tot ou tard, finiront par t'exploser à la figure parce que tu auras décidé de ne pas respecter un principe "simple".

    Personnellement, j'estime qu'il y a déjà largement d'autres possibilités de se tirer une balle dans le pied sans en rajouter en prenant des décisions conceptuelles inadaptées

    Et je parle par expérience en disant qu'une liberté prise dans le respect de LSP aura fatalement des répercussions dans l'avenir
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #31
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    Enlève sous-typage, c’est de trop dans le cas du duck typing.
    Vraiment ! J'ai tendance de plus en plus à considérer le duck typing (au moins dans les langages, généralement à typage dynamique, qui le supporte de base) comme du sous-typage structurel, le type de base étant le minimum attendu.

  12. #32
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Le problème d'une vision pratique de ce que l'on peut faire (comprends :de ce que le langage permet par défaut, car n'étant pas armé pour vérifier que tu tu as pris une décision "conceptuellement correcte"), c'est que C++ étant particulièrement permissif, il t'autorisera de faire certaines choses, conceptuellement fausses, qui, tot ou tard, finiront par t'exploser à la figure parce que tu auras décidé de ne pas respecter un principe "simple".

    Et je parle par expérience en disant qu'une liberté prise dans le respect de LSP aura fatalement des répercussions dans l'avenir

    tu m'obliges à me répéter... : on est entièrement d'accord => j'avais même effacé cette phrase de mon message avant de le poster : "si en pratique on est amené à ne pas respecter LSP pour obtenir une conception "intéressante", c'est qu'on est passé à côté de notre phase de conception." Mais bon j'étais tellement curieux d'avoir un exemple contraire car je n'en visualisais pas, que j'ai enlevé cette phrase un peu titillante, pour être sur d'avoir une réponse.


    Personnellement, j'estime qu'il y a déjà largement d'autres possibilités de se tirer une balle dans le pied sans en rajouter en prenant des décisions conceptuelles inadaptées
    M'en parle pas.. j'arrive même plus à compter mes orteils

    Sinon, j'aimerais toujours avoir un exemple concret de conception intéressante ne respectant pas LSP, car je n'en vois pas personnellement.
    Nullius in verba

  13. #33
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Kaamui Voir le message
    Pour LSP, il est clair que tu as tort. LSP parle avant tout de la notion d'héritage, ça saute aux yeux.
    Pour être tout à fait précis, LSP se réfère au sous-typage pas forcément à l'héritage (pour rester dans les langages mainstreams : l'implémentation d'une interface en Java défini, tout comme l'héritage, un sous-type mais l'héritage privé en C++ absolument pas).

  14. #34
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    D'où mon message du 24 mai à 17h46, qui n'avait pas été relevé.

  15. #35
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par Kaamui Voir le message
    Pour LSP, il est clair que tu as tort. LSP parle avant tout de la notion d'héritage, ça saute aux yeux.
    LSP parle de substituabilité. Le fait qu’en c++, le seul moyen de faire de la substituabilité runtime soit l’héritage est un détail. LSP est tout aussi valable en javascript, sur des objets n’ayant pourtant aucun lien hiérarchique.

    Sinon c'est pas pour ça que je me permets d'écrire. Malgré nos avis divergents je suis intéressé par ce que tu as dis, et j'aimerais avoir un exemple de "possibilités de conception intéressantes" ne respectant pas le LSP (vraiment par curiosité hein, je n'ironise pas)
    Partout où tu n’as pas besoin de substituabilité, tu n’as pas besoin de respecter le LSP. Tu peux utiliser un héritage public (pour hériter de l’implémentation et l’interface) sans nécessairement te soucier de respecter le LSP. Ça peut être le cas si tu cherches à éviter le coût de la virtualité, ou pour des raisons de performance. Il est clair que chaque fois que c’est possible, il vaut mieux utiliser l’héritage privé ou protégé pour ce genre de cas.

    Par contre, je rejoins Koala sur un point : si tu t’assois sur des principes, tu prends aussi des risques en conséquence en cas de mauvaise utilisation. C’est pour cette raison qu’en général, on limite cela à des classes techniques, ou pour des raisons de performance justifiées dans du code bien documenté.

  16. #36
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    LSP parle de substituabilité. Le fait qu’en c++, le seul moyen de faire de la substituabilité runtime soit l’héritage est un détail. LSP est tout aussi valable en javascript, sur des objets n’ayant pourtant aucun lien hiérarchique.
    Ok aux temps pour moi.

    Partout où tu n’as pas besoin de substituabilité, tu n’as pas besoin de respecter le LSP. Tu peux utiliser un héritage public (pour hériter de l’implémentation et l’interface) sans nécessairement te soucier de respecter le LSP. Ça peut être le cas si tu cherches à éviter le coût de la virtualité, ou pour des raisons de performance. Il est clair que chaque fois que c’est possible, il vaut mieux utiliser l’héritage privé ou protégé pour ce genre de cas.
    Oui, j'me doute, mais je parlais bien sur de cas ou j'ai besoin de substituabilité, et où je n'aurais pas besoin de respecter le LSP, car j'avais l'impression que tu parlais de ça.
    Nullius in verba

  17. #37
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    LSP parle de substituabilité. Le fait qu’en c++, le seul moyen de faire de la substituabilité runtime soit l’héritage est un détail. LSP est tout aussi valable en javascript, sur des objets n’ayant pourtant aucun lien hiérarchique.
    LSP parle aussi d'héritage :

    Wikipedia
    article original de Liskov

  18. #38
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    Citation Envoyé par oodini Voir le message
    LSP parle aussi d'héritage :

    Wikipedia
    article original de Liskov
    [un peu HS]
    Waaw, je sais pas comment t'as pu avoir cette source mais c'est trop classe. Rien de mieux pour arrêter de spéculer et savoir enfin ce qu'il en est... (qui est-ce qui le lit et qui nous raconte ? )
    [/HS]

    [HS Total]
    Liskov s'appelle Barbara, ça devient intéressant
    [/HS Total]

    De toute façon quand on parle de substituabilité, j'ai envie de dire que c'est un principe dont "hérite l'héritage" (la vache on est en plein dans le sujet ^^). J'ai le droit ou c'est une bêtise ?

    edit : la conclusion résume bien.

    Citation Envoyé par Barbara Liskov
    We conclude that although data abstraction is more important, type hierarchy does extend its
    usefulness. Furthermore, inheritance is sometimes needed to express type hierarchy and is therefore a
    useful mechanism to provide in a programming language.
    Nullius in verba

  19. #39
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par Kaamui Voir le message
    Liskov s'appelle Barbara, ça devient intéressant
    Bof... :-)


  20. #40
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par oodini Voir le message
    LSP parle aussi d'héritage :

    Wikipedia
    article original de Liskov
    subtype ≠ subclass. L’héritage n’est pas une condition nécessaire. C’est important de voir que, même si le papier parle aussi de la mise en œuvre du LSP dans les langages objets et donc de l’héritage, il ne s’y limite pas, étant à un niveau d’abstraction supérieur.

    Après, c’est peut-être aussi moi qui vais un peu au-delà de l’énoncé initial. Mais ce n’est pas grave et je le maintiens , LSP ne saurait se limiter à l’héritage : un très bon exemple est les concepts c++.

    Oui, j'me doute, mais je parlais bien sur de cas ou j'ai besoin de substituabilité, et où je n'aurais pas besoin de respecter le LSP, car j'avais l'impression que tu parlais de ça.
    Un cas classique est le stockage d’objets différents dans un même conteneur. Mais c’est quand même un mauvais exemple : chaque fois qu’on peut, il vaut mieux utiliser un variant qu’une hiérarchie pour faire ça (malheureusement, en java ou c#, on n’a pas vraiment le choix).

Discussions similaires

  1. [JMerise] Version 0.3.9.7 - Héritage sans contrainte
    Par aidefz dans le forum JFreesoft
    Réponses: 3
    Dernier message: 14/09/2015, 15h31
  2. Modéliser héritage sans exclusion
    Par Lystik dans le forum UML
    Réponses: 3
    Dernier message: 31/07/2013, 17h24
  3. Implémentation héritage sans relation cyclique
    Par Driss35 dans le forum Diagrammes de Classes
    Réponses: 6
    Dernier message: 03/08/2012, 19h50
  4. [modelisation] Héritage sans nouvelles spécifications
    Par poukill dans le forum Diagrammes de Classes
    Réponses: 8
    Dernier message: 21/06/2007, 14h15
  5. Réponses: 3
    Dernier message: 04/06/2007, 08h34

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo