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 :

Ruptures des invariants en C++


Sujet :

Langage C++

  1. #1
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut Ruptures des invariants en C++
    Bonjour,
    J'ai une question concernant la rupture des invariants en C++. En général, on dit que les invariants s'appliquent à l'extérieur d'une classe : Meyer himself
    It describes a consistency property that every instance of the class must satisfy whenever it's observable from the outside
    Donc un objet est construit de façon à respecter ses invariants, ensuite chaque fois qu'une fonction est appelée en respect du contrat, les invariants sont toujours respectés lorsque l'appelant récupère l'objet.
    En revanche, à l'intérieur d'une fonction, l'invariant peut être momentanément rompu du moment qu'il est rétabli avant la fin de la fonction.
    Soit une classe C, dans une fonction F1, au moment où un invariant est rompu, sémantiquement :
    -> (1) F1 a-t-elle le droit d'appeler une méthode publique (non virtuelle) C::F2 sans rétablir les invariants ? J'aurais tendance à dire que non, car cette méthode étant publique, le respect des invariants est une précondition implicite.
    -> (2) F1 a-t-elle le droit d'appeler une méthode privée (non virtuelle) C::F2 sans rétablir les invariants ? J'aurais tendance à dire que oui, car cette méthode étant privée, seule C peut l'appeler et éventuellement savoir qu'elle peut l'appeler sans respecter certains invariants. On est dans le cas où non 'observable from the outside".
    -> (3) F1 a-t-elle le droit d'appeler une méthode protégée (non virtuelle) C::F2 sans rétablir les invariants ? Je ne sais pas trop quoi répondre car cette méthode n'est pas accessible à l'extérieur mais elle l'est par les classes dérivées.
    Rajoutons l'héritage et supposons que C dérive de B :
    -> (4) F1 peut-elle appeler une méthode publique (non virtuelle) de la classe B sans rétablir les invariants ? J'aurais envie d'avoir la même analyse que pour (1)
    -> (5) F1 peut-elle appeler une méthode protégée (non virtuelle) de la classe B sans rétablir les invariants ? Idem qu'en (3), je ne sais pas trop quoi en penser.
    Maintenant, supposons une classe D dérivant de C.
    La résolution dynamique de l'appel d'une fonction virtuelle ne tient pas compte de la visibilité de la fonction virtuelle, ni dans la classe C, ni dans la classe dérivée D. D'où
    -> (6) F1 peut-elle appeler une méthode virtuelle sans rétablir les invariants ?
    -> (7) Faut-il dissocier fonctions virtuelles pures ou non ?

    En fait, les questions (3), (5) et (6) ( (7) devrait être une conséquence de la réponse aux trois premières), à mon avis, trouvent leur réponse dans une question plus générale :
    Est-ce qu'une classe de base B ou une classe dérivée D de la classe C est externe à C ?
    Si la réponse est oui, alors F1 doit rétablir les invariants dans les cas (3), (5) et (6) et il n'y a pas de différence que les fonctions soit virtuelles pures ou juste virtuelles.
    Si la réponse est non, alors quelles sont les conditions valides pour les différents appels ?
    Et, là j'avoue que je ne sais pas trop quoi répondre. J'ai envie d'avoir une approche 'forte' (répondre 'oui') qui implique alors que :
    -> (8) Pour un appel d'une fonction non virtuelle d'une classe de base, les invariants de la classe de base et de la classe C doivent être respectés ;
    -> (9) Pour un appel d'une fonction virtuelle (donc potentiellement d'une spécialisation de la classe dérivée), les invariants de la classe C doivent être respectés.

    On pourrait avoir une approche plus souple concernant (8) en disant que seuls les invariants de la classe de base doivent être respectés. Mais si celle-ci fait appel à une méthode virtuelle, elle peut être amenée à appeler une fonction d'une classe dérivée sans respecter les invariants. Quid ?

    Le pattern NVI a l'avantage de dissocier le contrat de la classe vis-à-vis de l'extérieur stricto-sensu d'un côté et de classes dérivées de l'autre. En ce sens, l'utilisation de ce pattern peut apporter des souplesses : le non-respect d'un invariant peut faire partie du contrat de la fonction virtuelle. En généralisant un peu, est-ce que la portée de la fonction virtuelle si elle est ignorée syntaxiquement ne devrait pas être prise en compte dans le contrat pour savoir quelle politique appliquée sur les invariants ? Difficile de répondre car une classe dérivée peut changer la portée d'une fonction.
    Je n'ai pas vraiment trouvé de réponses satisfaisantes à ces interrogations. J'aurais aimé connaître votre vision de cet aspect ?
    Je ne sais pas si j'ai été suffisamment clair...

  2. #2
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    1/ Je dirais oui : F1 a été conçue en même temps que F2, elle a le droit d'appeler F2 si elle sait (chose qu'un autre utilisateur de F2 ne peut pas savoir) que F2 ne fait pas appel à l'invariant.
    2/ et 3/ : Idem

    4/ et 5/ : Si la classe a cassé ses invariants propres, sans casser ceux de B, aucun problème. Si elle a cassé les invariants de B (mais peut-elle vraiment le faire ? N'y a-t-il pas un problème de conception de B ?), je dirais qu'elle peut appeler la fonction, si B et C sont définis dans le même module (voir justificatif du 1/), mais pas dans le cas général.

    6/ et 7/ J'hésiterais fortement avant de le faire... Virtuelle ou virtuelle pure, je ne vois pas d'écart.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  3. #3
    Membre confirmé Avatar de Lavock
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 560
    Points : 633
    Points
    633
    Par défaut
    Je suis ok avec JolyLoic.

    Pour 7/ Je dirai que non aucune différence.

    Après, 6/ pas facile... D'un côté les classes filles ne sont pas censées pouvoir modifié les invariants, mais tu ne sais pas se qu'elles font, et elles peuvent très bien faire appelle a une méthode publique, qui à besoin de l'invariant...

    Si on applique ta proposition, ça en revient à interdire des fonctions aux classes filles, celles qui ont besoin de l'invariant, alors qu'en même temps elles ne sont pas censées le connaître... Cela me semble un peu paradoxal.
    The mark of the immature man is that he wants to die nobly for a cause, while the mark of the mature man is that he wants to live humbly for one.
    --Wilhelm Stekel

  4. #4
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    -> (1) F1 a-t-elle le droit d'appeler une méthode publique (non virtuelle) C::F2 sans rétablir les invariants ? J'aurais tendance à dire que non, car cette méthode étant publique, le respect des invariants est une précondition implicite.
    Si F2 est publique, j'aurais tendance à dire "non" également.

    Citation Envoyé par 3DArchi Voir le message
    -> (2) F1 a-t-elle le droit d'appeler une méthode privée (non virtuelle) C::F2 sans rétablir les invariants ? J'aurais tendance à dire que oui, car cette méthode étant privée, seule C peut l'appeler et éventuellement savoir qu'elle peut l'appeler sans respecter certains invariants. On est dans le cas où non 'observable from the outside".
    Idem : oui, à condition EXPRESSE que F2 ne s'appuie pas sur l'invariant en entrée (ex : fonction de rétablissement de l'invariant, par exemple, de type "normalisation"...).

    Citation Envoyé par 3DArchi Voir le message
    -> (3) F1 a-t-elle le droit d'appeler une méthode protégée (non virtuelle) C::F2 sans rétablir les invariants ? Je ne sais pas trop quoi répondre car cette méthode n'est pas accessible à l'extérieur mais elle l'est par les classes dérivées.
    Je dirais oui. Une classe dérivée est censée connaître un minimum le comportement de sa classe ancêtre... Toutefois, on pourrait surtout se poser la question de la pertinence de laisser en protected (et non pas private) une fonction "dangereuse"...

    Citation Envoyé par 3DArchi Voir le message
    Rajoutons l'héritage et supposons que C dérive de B :
    -> (4) F1 peut-elle appeler une méthode publique (non virtuelle) de la classe B sans rétablir les invariants ? J'aurais envie d'avoir la même analyse que pour (1)
    Il faut distinguer deux parties de l'invariant : celui hérité de B, et celui propre à C.
    Pour celui hérité de B : Non, elle n'a pas le droit, car comme tu le soulignes, on en revient au point 1.
    Pour son invariant propre : B ne le connait pas, donc on s'en fout, tu fais ce que tu veux avec l'invariant propre quand tu appelles une méthode ancêtre.

    Citation Envoyé par 3DArchi Voir le message
    -> (5) F1 peut-elle appeler une méthode protégée (non virtuelle) de la classe B sans rétablir les invariants ? Idem qu'en (3), je ne sais pas trop quoi en penser.
    Pour moi, même réponse qu'au point 1, avec la nuance de séparation d'invariant du point 4. Le fait que ce soit une classe ancêtre ne change rien au problème à mon avis.

    Citation Envoyé par 3DArchi Voir le message
    -> (6) F1 peut-elle appeler une méthode virtuelle sans rétablir les invariants ?
    J'aurais tendance à regarder la visibilité au niveau de C, et à appliquer la réponse des points 1, 2 ou 3 (plus séparation d'invariant du 4) en fonction de cette visibilité... Si la classe dérivée change ladite visibilité et/ou ne respecte pas les points précédents, ben tant pis pour elle.

    Citation Envoyé par 3DArchi Voir le message
    -> (7) Faut-il dissocier fonctions virtuelles pures ou non ?
    Pas à mon sens. Pour moi, une méthode virtuelle pure n'a qu'un seul but : définir une interface qui n'a pas (ou ne peut pas) avoir de traitement "commun" à toutes les instances dérivées. Mais quel que soit le cas, elle reste définie par la classe mère, et le fait qu'elle ne soit pas implémentée réellement ne change rien au fond du problème.

    Citation Envoyé par 3DArchi Voir le message
    En fait, les questions (3), (5) et (6) ( (7) devrait être une conséquence de la réponse aux trois premières), à mon avis, trouvent leur réponse dans une question plus générale :
    Réponse de normand : oui et non...

    Je précise : une classe mère, bien que théoriquement en boîte noire, doit malgré tout être un minimum connue par les classes qui en dérivent, tout comme c'est le cas pour une classe friend. On peut donc éventuellement jouer avec l'invariant si l'on sait ce que l'on fait, même si cela reste très fortement déconseillé.
    Par contre, on n'a pas à se préoccuper de l'implémentation des classes filles dans une classe donnée. On doit par contre fournir une interface claire et bien définie : si la classe fille ne les respecte pas, ce n'est pas la faute de la classe mère mais plutôt du goret qui a dérivé la classe !

    Citation Envoyé par 3DArchi Voir le message
    En généralisant un peu, est-ce que la portée de la fonction virtuelle si elle est ignorée syntaxiquement ne devrait pas être prise en compte dans le contrat pour savoir quelle politique appliquée sur les invariants ? Difficile de répondre car une classe dérivée peut changer la portée d'une fonction.
    Pour moi, seule la visibilité de la méthode lors de sa définition initiale (= donc, dans la classe ancêtre la plus éloignée possible) doit compter, et oui, la portée devrait être LE critère pour maintenir l'invariant ou non... Tout comme il est évident, dans le cas d'une classe partagée entre plusieurs threads, que tout état instable (invariant rompu) DOIT être atomique, si besoin à coup de mutex.

    Donc, de ce point de vue, seules les méthodes publiques doivent garantir l'invariant, les méthodes privées pouvant s'en passer pour des raisons d'algorithme et/ou de performances. Le cas des méthodes protégées est, justement, intermédiaire et doit être considéré avec circonspection. Dans le doute, mettre la fonction en privé, avec un wrapper conservant l'invariant protégé accessible par les classes dérivées (la classe mère pouvant utiliser la version privée plus efficace si besoin).

    Le cas du changement de visibilité est un peu particulier à mon sens, et il faut réfléchir un poil sur les raisons d'un tel changement... Dans le cas classique (public), il n'y a pas de problèmes bien entendu.
    Dans les autres cas (protected ou private), je pense qu'il est crucial de comprendre le but d'une restriction de portée : est-ce pour masquer la classe ancêtre ? L'encapsuler totalement ? Changer une interface publique ?
    Dans tous ces cas, la question ne se pose plus vraiment, la classe ancêtre devenant un "outil" que l'on plie à ses besoins. On adapte donc le code aux particularités de la classe ancêtre et on masque quasi-totalement les fonctions héritées. De plus, une classe ancêtre est rarement conçue pour être ainsi noyautée au sein d'une classe fille, et l'ancêtre ne peut donc pas être conçue non plus pour tenir compte de la restriction de portée de ses propres méthodes : elle continuera d'appliquer la politique d'invariant initiale, en fonction de la portée initiale des méthodes.

    Maintenant, pour d'autres raisons que celles précitées, je ne sais pas trop... Faudrait des exemples, à mon avis, pour pouvoir "trancher".
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Bonjour et merci pour ces réponses.
    Il y a quelque chose qui me gène dans le fait de pouvoir casser les invariants de C (mais pas ceux de B) avant l'appel d'une méthode de la classe de base B : cette méthode de B peut très bien avoir un appel virtuel qui fera redescendre l'exécution vers C alors que tous les invariants ne sont pas là. C'est pour ça que je me demandais s'il ne fallait pas être 'sévère' et toujours respecter les invariants dès qu'on sort de la classe C stricto-sensu (par le bas -> fonction virtuelle, ou par le haut -> fonction de la classe de base).
    Il y a un point qui devait être ambigüe dans ma question car vous avez été 2 à me dire de ne pas s'occuper de la classe dérivée. En aucun cas, la classe C ne se pose de question sur la classe dérivée. C'est juste qu'avec une fonction virtuelle, la fonction effectivement appelée n'est plus forcément celle de C, mais celle de la classe dérivée. Ma question est donc, comme on sort de la classe, ne faut-il pas rétablir les invariants avant d'appeler une fonction virtuelle ?

  6. #6
    Membre confirmé Avatar de Lavock
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 560
    Points : 633
    Points
    633
    Par défaut
    Erf, j'avais mal lu la réponse de JolyLoic >< (décidément).

    En complément, étant donnée que l'on peut avoir une jolie chaine, bien que peu probable, fille->mère->virtuelle, à moins de connaître l'implémentation de la mère, de savoir qu'il n'y a pas de fonction virtuelle, ou de n'avoir aucune fonction virtuelle ayant besoin de l'invariant, il est nécessaire de rétablir aussi les invariant de C dans les cas 4/ et 5/.
    The mark of the immature man is that he wants to die nobly for a cause, while the mark of the mature man is that he wants to live humbly for one.
    --Wilhelm Stekel

  7. #7
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Bonjour et merci pour ces réponses.
    Il y a quelque chose qui me gène dans le fait de pouvoir casser les invariants de C (mais pas ceux de B) avant l'appel d'une méthode de la classe de base B : cette méthode de B peut très bien avoir un appel virtuel qui fera redescendre l'exécution vers C alors que tous les invariants ne sont pas là. C'est pour ça que je me demandais s'il ne fallait pas être 'sévère' et toujours respecter les invariants dès qu'on sort de la classe C stricto-sensu (par le bas -> fonction virtuelle, ou par le haut -> fonction de la classe de base).
    Il y a un point qui devait être ambigüe dans ma question car vous avez été 2 à me dire de ne pas s'occuper de la classe dérivée. En aucun cas, la classe C ne se pose de question sur la classe dérivée. C'est juste qu'avec une fonction virtuelle, la fonction effectivement appelée n'est plus forcément celle de C, mais celle de la classe dérivée. Ma question est donc, comme on sort de la classe, ne faut-il pas rétablir les invariants avant d'appeler une fonction virtuelle ?
    J'aurais tendance à dire qu'être strict ne fera pas de mal et que l'on ne peut pas toujorus faire confiance aux classes filles. Imaginons un développeur de Qt qui bosse sur un widget qui possède un invariant bien précis. Il ne peut pas faire confiance aux dizaines de milliers de développeurs qui vont hériter de son widget pour le personnaliser/compléter. A sa place j'ensure-rerais l'invariant directement au niveau de la classe de base (avec des choses en private que l'on ne pourrait pas retoucher dans la classe fille par exemple, cf le message de Mac LAK), etc.

    Après, dans la vraie vie, on a rarement le temps de se poser, de lister les invariants de sa classe, de bien réfléchir à toutes les utilisations possibles de sa classe + ce qu'on pourrait faire en héritant de cette dernière, etc. Il faut souvent faire au mieux au plus vite.

  8. #8
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    cette méthode de B peut très bien avoir un appel virtuel qui fera redescendre l'exécution vers C alors que tous les invariants ne sont pas là.
    D'où le fait de se poser la question par rapport à la visibilité de la classe MÈRE... Si la fonction est potentiellement dangereuse, ne pas la laisser publique (ni même protégée) mais la passer en privé, quitte à faire un wrapper au dessus qui respecte à coup sûr l'invariant.

    Citation Envoyé par 3DArchi Voir le message
    C'est pour ça que je me demandais s'il ne fallait pas être 'sévère' et toujours respecter les invariants dès qu'on sort de la classe C stricto-sensu (par le bas -> fonction virtuelle, ou par le haut -> fonction de la classe de base).
    Tu peux, bien sûr, mais cela a un coût en terme de taille de code et de vitesse d'exécution... Ce coût peut être négligeable ou pas, voire être carrément critique : tout dépend de ce que tu fais, où tu le fais et sur quel genre de machine.

    Donc, pour moi, c'est quelque chose qui doit être réfléchi en fonction de la cible prévue, et des besoins expressément exigés par l'utilisateur final. Tu peux avoir des classes ultra-strictes (ex : classes de gestion de haut niveau, genre vérification d'une saisie IHM), ou d'autres qui n'en font aucune (classes de gestion très bas niveau). Entre les deux, toutes les variantes possibles.

    Mais on pourrait généraliser aussi le principe de visibilité : la classe est-elle "publique" au niveau de l'application (= en "contact" avec l'extérieur de l'application, comme un fichier ou un humain), "protégée" (difficilement visible et/ou restreint à un admin), ou carrément "privée" (fonction interne que toi seul appelle) ? En fonction de cette "visibilité" de la classe elle-même, tu en déduis ce que tu as le droit de faire (ou non !!) côté respect des invariants lors d'un appel potentiellement externe (inclus méthodes virtuelles, donc).

    Citation Envoyé par 3DArchi Voir le message
    C'est juste qu'avec une fonction virtuelle, la fonction effectivement appelée n'est plus forcément celle de C, mais celle de la classe dérivée. Ma question est donc, comme on sort de la classe, ne faut-il pas rétablir les invariants avant d'appeler une fonction virtuelle ?
    Seulement en fonction de la visibilité initiale de la méthode ! Parce que même si la méthode n'est pas celle de la classe de base, la classe dérivée est censée savoir utiliser (ou ne PAS utiliser) la méthode ancêtre afin d'assurer un traitement correct des informations.
    Donc, elle doit savoir comment fonctionne cette méthode, y compris sur le non-respect de l'invariant. Là, par contre, c'est du boulot de documentation et non plus de codage.

    De même, tu ne peux pas interdire à une classe fille d'appeler la méthode ancêtre, ni l'obliger à le faire non plus d'ailleurs. C'est donc forcément de la responsabilité du dév qui code la classe fille de respecter TON interface, et c'est de TA responsabilité de mentionner explicitement les ruptures d'invariant sur les méthodes (forcément et uniquement les non-publiques, bien entendu...)
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  9. #9
    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
    J'ai parcouru rapidement les réponses déjà faites, mais :

    (1) F1 a-t-elle le droit d'appeler une méthode publique (non virtuelle) C::F2 sans rétablir les invariants ? J'aurais tendance à dire que non, car cette méthode étant publique, le respect des invariants est une précondition implicite.
    Non, pour la raison que tu donnes.

    -> (2) F1 a-t-elle le droit d'appeler une méthode privée (non virtuelle) C::F2 sans rétablir les invariants ? J'aurais tendance à dire que oui, car cette méthode étant privée, seule C peut l'appeler et éventuellement savoir qu'elle peut l'appeler sans respecter certains invariants. On est dans le cas où non 'observable from the outside".
    Oui, là aussi pour la raison que tu donnes.

    -> (3) F1 a-t-elle le droit d'appeler une méthode protégée (non virtuelle) C::F2 sans rétablir les invariants ? Je ne sais pas trop quoi répondre car cette méthode n'est pas accessible à l'extérieur mais elle l'est par les classes dérivées.
    Non, les méthodes protégées font partie de l'interface "publique" et sont donc soumises aux mêmes règles que les méthodes publiques en ce qui concerne les invariants de classe.

    Rajoutons l'héritage et supposons que C dérive de B :
    -> (4) F1 peut-elle appeler une méthode publique (non virtuelle) de la classe B sans rétablir les invariants ? J'aurais envie d'avoir la même analyse que pour (1)
    Je suis d'accord.

    -> (5) F1 peut-elle appeler une méthode protégée (non virtuelle) de la classe B sans rétablir les invariants ? Idem qu'en (3), je ne sais pas trop quoi en penser.
    Non, comme en 3 .

    Maintenant, supposons une classe D dérivant de C.
    La résolution dynamique de l'appel d'une fonction virtuelle ne tient pas compte de la visibilité de la fonction virtuelle, ni dans la classe C, ni dans la classe dérivée D. D'où
    -> (6) F1 peut-elle appeler une méthode virtuelle sans rétablir les invariants ?
    Le seul cas litigieux, c'est celui d'une méthode virtuelle privée. Pour les autres, c'est non pour les raisons invoquées plus haut. Je suis toujours dubitatif quand je vois une méthode de ce type, et je pense que non, il ne faut pas. En effet, il faut informer celui qui redéfinit la méthode des préconditions, et donc, du non respect de l'invariant (sinon, il peut à juste titre supposer que l'invariant est respecté, et donc restreindre les préconditions, ce qui est interdit). Or, je ne connais aucun moyen de l'informer correctement de ceci.

    -> (7) Faut-il dissocier fonctions virtuelles pures ou non ?

    Je ne vois aucune raison de faire ça.

  10. #10
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    C'est intéressant, je remarque que presque tout le monde a un avis différent du mien. Pourtant, je ne pense pas avoir dit de bêtises.
    Pour le 1, par exemple, la classe Population a entre autres l'invariant :
    - Soit monPoidsMoyenOk est false, soit monPoidsMoyen contient le poids moyen de la population (mise en place d'un système de cache).

    Et j'ai une fonction AjouteIndividu :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    mesIndividus.push_back(individu);
    // Là, l'invariant n'est plus respecté
    cout << "La population contient désormais " << taille() << " individus" << endl;
    monPoidsMoyenOk = false;
    // Là, l'invariant est rétabli
    Dans cette fonction, j'appelle une fonction publique taille au moment où l'invariant est cassé. Est-ce vraiment problématique ? En tant qu'implémenteur de la classe population, je sais que taille ne dépend pas de cet invariant là (mais va simplement retourner mesIndividus.size()). Pourquoi devrais-je intervertir les deux dernières lignes de code ? (dans ce cas là, c'est peu coûteux de le faire, mais est-ce toujours le cas ?)

    Je pense que dire que l'invariant est une pré-condition implicite de toutes les fonctions publiques est faux. Les pré-conditions sont des conditions à respecter par le client, les invariants sont à respecter par l'implémenteur. On peut juste dire à mon avis que les invariants sont des post-conditions implicites des fonctions publiques appelées par un client.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  11. #11
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    C'est intéressant comme point de vue : l'invariant est toujours une post-condition (modulo l'exception), et ne serait une pré-condition (de facto) que lors de l'appel d'une méthode sur un objet autre que this. Ca j'arrive à le concevoir pour une fonction non virtuelle. Maintenant, si taille est virtuelle, ne faudrait-il pas rétablir l'invariant car alors je n'ai pas la main sur l'implémentation effective. Les fonctions virtuelles n'étant plus alors vues comme un appel 'interne' de l'objet ?

  12. #12
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Si taille est virtuelle, et que j'expose ma hiérarchie de classe à l'extérieur de mon module, assurément. Si elle est interne à mon module, je serais plus hésitant. Je sais que cette notion de module n'existe pas vraiment en C++, mais conceptuellement, elle existe bien.

    Par exemple, pour mettre en place un algo, j'utilise le DP strategy, car selon les cas, un mode de fonctionnement ou un autre sera plus approprié. J'ai donc dans mon code une classe de base et n classes dérivées, une par stratégie possible. Et je ne veux pas permettre à un client d'ajouter sa propre stratégie (ne serait-ce que parce que ça me forcerait à publier et maintenir au cours du temps le code de la classe de base et ses fonctions virtuelles). Du coup, le code client n'aura à sa disposition qu'une façade où les stratégies seront représentées par un enum.

    Dans ce genre de situation, j'ai tendance à considérer toutes les classes dérivées possibles (qui sont en nombre fini et contrôlées par moi) comme faisant partie en terme de connaissance et de restriction d'accès de ma classe de base. Et dans ce cas, appeler une fonction virtuelle alors que l'invariant est rompu ne me gêne pas trop.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  13. #13
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    En fait c'est, si je me souvient bien, comme pour la loi de Demeter. Tu ne raisonnes pas au niveau de la classe, mais du 'module'. Les classes non exposées sont vues alors comme des membres privées et relèvent du détail d'implémentation. En poussant, le seul contrat qui existe est celui défini par la façade alors.

  14. #14
    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
    Dans cette fonction, j'appelle une fonction publique taille au moment où l'invariant est cassé. Est-ce vraiment problématique ? En tant qu'implémenteur de la classe population, je sais que taille ne dépend pas de cet invariant là (mais va simplement retourner mesIndividus.size()). Pourquoi devrais-je intervertir les deux dernières lignes de code ? (dans ce cas là, c'est peu coûteux de le faire, mais est-ce toujours le cas ?)
    En fait, il faut quand même se poser la question de : « à quoi sert mon invariant ? »

    Si mon invariant est une pré/post condition universelle, alors les réponses sont celles que j'ai données. Et pour répondre à ton problème, je mettrai l'implémentation de la fonctionnalité « ajouter individu » dans une fonction privée.

    Si les invariants sont là comme un outil de vérification de la correction de l'écriture de la classe (auquel cas ils ne sont pas nécessairement publics), alors ton point de vue est approprié.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Utilisation des invariants pour la recherche des images par le contenu
    Par sarrainf dans le forum Traitement d'images
    Réponses: 3
    Dernier message: 27/03/2009, 13h09
  2. Réponses: 1
    Dernier message: 17/10/2007, 12h29
  3. Inserer des ruptures Excel VBA
    Par hka75 dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 02/04/2007, 20h00
  4. [Maths] Calculs invariants à des transformations
    Par mathieu_t dans le forum Mathématiques
    Réponses: 16
    Dernier message: 26/04/2006, 18h50
  5. Réponses: 3
    Dernier message: 26/04/2006, 17h06

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