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

C++ Discussion :

[POO] Méthodes virtuelles const ?


Sujet :

C++

  1. #1
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Points : 1 051
    Points
    1 051
    Par défaut [POO] Méthodes virtuelles const ?
    Voila, il est plus de minuit et demi maintenant et je me pose une question existencielle :

    Les méthodes d'interface et autres méthodes virtuelles "ont-elles le droit" d'être déclarées const?

    A moins de cas particuliers, mais je n'en vois pas, même en cherchant, c'est à l'implémentation de décider si, oui ou non, la méthode redéfinie va être amenée à modifier l'objet.

    Quel est votre avis la-dessus ?

  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 : 50
    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
    Oui, elles en ont le droit. Être const fait partie du contrat, et c'est la classe de base qui fixe le contrat. C'est elle qui dit qu'une fonction aura N paramètres de tels ou tels types, c'est aussi elle qui dit si on pourra l'appeler sur un objet constant.

  3. #3
    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 861
    Points
    11 861
    Par défaut
    Exemple "bête" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class GraphicObject
    {
      public :
        virtual void Draw() const = 0;
    };
     
    class Ball : public GraphicObject
    {
      public : 
        void Draw() const { /* on affiche la balle */ }
    }
    La méthode Draw ne modifiant pas l'instance, on peut la "const-qualifier", et ainsi elle pourra être appelée sur des objets constants, tout en préservant le polymorphisme désiré.

  4. #4
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Points : 1 051
    Points
    1 051
    Par défaut
    Bonjour,

    si j'écris ton exemple tel quel, Alp, j'ai l'erreur "cannot instanciate abstract class" car la méthode Draw de Ball n'est justement pas const alors que celle GraphicObject est déclarée comme l'étant.

    Et c'est bien là le fond du problème. Je suis tout à fait d'accord avec vous pour dire que décider de rendre une méthode virtuelle const établi un contrat qui doit suivre une logique et que cela doit être fait lors du design de la classe abstraite de base.
    Seulement je trouve pleins de cas où cela pose problème.

    Si je reprends l'exemple de l'objet graphique et sa méthode Draw() : l'objet est dans un certain état (position, couleur etc.) et on lui demande de s'afficher en l'état, à priori sans le modifier, de façon à ce que si l'on appelle à nouveau cette méthode juste derrière, le même affichage devrait nous être donné. Donc notre méthode est const vis à vis de l'objet graphique.

    Alp, tu as surement raisonné ainsi et j'aurais certainement fait pareil. Mais toujours en continuant l'exemple, imaginons que j'implémente ObjetGraphique et mon implémentation nécessite beaucoups de précalculs non négligeables avant de pouvoir effectivement se dessiner. Ces calculs dépendent de l'état de l'objet et ne devraient pas avoir à être ré-éxécutés si l'état n'a pas changé depuis le dernier appel à Draw(). Et bien, à part en déclarant des variables mutable, je ne peux pas sauver mes précalculs dans Draw().

    Voilà ce qui me chiffone depuis hier.

  5. #5
    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 861
    Points
    11 861
    Par défaut
    En fait j'ai simplement oublié le const...

    Car j'étais fatigué

    Mais tu peux pas casser la qualification const, effectivement. J'édite le code ci-dessus

  6. #6
    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 : 50
    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
    Citation Envoyé par NiamorH Voir le message
    imaginons que j'implémente ObjetGraphique et mon implémentation nécessite beaucoups de précalculs non négligeables avant de pouvoir effectivement se dessiner. Ces calculs dépendent de l'état de l'objet et ne devraient pas avoir à être ré-éxécutés si l'état n'a pas changé depuis le dernier appel à Draw(). Et bien, à part en déclarant des variables mutable, je ne peux pas sauver mes précalculs dans Draw().
    Et justement, tu peux les déclarer mutable, et alors, ça marche. Je ne vois pas trop ce qui te gène dans ce cas.

    Et le mutable n'est pas là "pour faire plaisir" à la classe de base : Il est là parce que, indépendamment de toute autre chose, ton objet n'est pas sémantiquement modifié par son affichage, le fait qu'il y ait des pré-calculs mis en cache n'étant qu'un détail d'implémentation ne devant pas apparaître dans l'interface.

  7. #7
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 382
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 382
    Points : 41 593
    Points
    41 593
    Par défaut
    Pour moi, un truc qui "aide" à comprendre les variables mutables, c'est de se dire que ce ne sont pas des variables à part entière de la classe, juste des détails d'implémentation.
    Ce qui est le cas pour la plupart des utilisations de variables mutables: Comptage de références intrusif, cache, et compteurs d'appels...

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 632
    Points : 30 714
    Points
    30 714
    Par défaut
    Salut,

    Le role d'une méthode const est de préciser au compilateur "cette méthode ne modifie pas l'objet".

    Par le fait de modifier l'objet, il faut comprendre le fait de modifier les "propriétés intrinsèques" qui font que ta balle rouge et bleue d'un diametre de N n'est pas une balle verte et jaune d'un diamètre de 2N, et que, au moment de l'appel, sa position et son "orientation" (le fait que le rouge soit en haut à gauche et le bleu en bas à droite) se seront pas modifiée.

    Par contre, si pour t'éviter d'avoir à recalculer en permanence l'aspect à donner à ta balle, tu décide de mettre cet aspect dans un cache, tu te trouve exactement dans le cas d'utilisation d'une variable mutable: ca ne modifie absolument pas les propriétés intrinsèques de la balle, cela ne modifie que la manière dont la méthode d'affichage va représenter la balle dans la situation donnée.

  9. #9
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Points : 1 051
    Points
    1 051
    Par défaut
    Effectivement, le mot clef mutable est la solution que j'utilise. Mais ça m'a toujours géné d'y avoir recours. Dans ma tête je me suis toujours dis "tu as dû mal penser quelque chose pour en arriver à devoir t'en servir."

    Peut être que je me fais du mauvais sang pour rien après tout.

  10. #10
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 382
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 382
    Points : 41 593
    Points
    41 593
    Par défaut
    Je me disais la même chose au début, c'est d'ailleurs pourquoi j'ai restreint mon utilisation de mutable au comptage de références intrusif et aux caches...

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 632
    Points : 30 714
    Points
    30 714
    Par défaut
    Effectivement, tu te fais beaucoup de mauvais sang pour rien...

    Bien sur, comme beaucoup de choses en programmation, il faut toujours se poser la question de savoir si oui ou non il est opportun de déclarer un membre mutable...

    Bien sur, l'abus de déclaration de membres mutables est au minimum le signe d'une réflexion sans doute erronée.

    Mais, une fois que tu a clairement défini qu'un membre agit comme un cache, comme une machine à état qui peut devenir invalide à n'importe quel moment (y compris au sein de méthodes constantes), et que la modification de ce membre ne modifie en rien les "propriétés intrinsèques" de l'objet en cours, tu es clairement dans le cadre dans lequel il est "opportun" de déclarer le membre mutable

  12. #12
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Points : 1 051
    Points
    1 051
    Par défaut
    Oui mais c'est aussi parce qu'on est parti d'un exemple qui s'y prétait déjà bien : la balle se dessine et met à jour un cache de précalculs pour la fois suivante. Le cache n'a d'utilité que pour cette méthode Draw(), au pire une ou deux autres méthodes de l'objet, ok je passe mon cache en mutable, c'est bateau.

    Je vous donne un autre exemple, toujours fictif bien que très inspiré, où cette fois c'est beaucoup moins évident.

    Mon programme est un lecteur audio style winamp. J'ai une classe Lecteur qui dispose d'une propriété "Position" permettant de savoir à tout moment où en est la lecture.
    La mise à jour de Position déclenche un évènement qui demande à l'interface de rafraîchir la barre de progression de la piste audio.

    La classe Lecteur implémente l'interface IDataFiller permettant au moteur audio de démander régulièrement des échantillons à envoyer à la carte son. La signature de la méthode à implémenter est : void Fill( char* data, size_t dataSize ) const.
    Le lecteur attentif aura remarqué le fameux const

    A priori tout ce qu'est sensé faire le DataFiller (mon Lecteur) c'est de remplir un buffer, donc, pas de souci, la méthode est en droit d'être const.
    Seulement le Lecteur veut également en profiter pour mettre à jour la propriété Position parce que, après tout, il a avancé dans la lecture.

    Résultat : J'ai une propriété tout à fait légitime de ma classe Lecteur qui doit passer mutable.

    Je pense que cet exemple est bien plus parlant pour comprendre mon souci d'origine car, cette fois, ce qui doit passer mutable, c'est une variable essentielle et exposée publiquement par la classe Lecteur.

    Lorsque JolyLoic parle de la sémantique d'une méthode, j'en suis presque amené à penser que les mécanismes du polymorphisme peuvent changer cette sémantique, qu'elle n'est plus la même en fonction du contexte d'où on l'étudie.

    Contexte moteur audio -> méthode Fill n'a qu'un seul but qui ne modifie pas l'objet
    Contexte lecteur audio -> méthode Fill a un autre but modifiant l'objet

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 632
    Points : 30 714
    Points
    30 714
    Par défaut
    En fait, la position dans le flux n'est pas une "propriété intrinsèque" du flux de données audio...
    Les propriété intrinsèques sont:
    • le titre de l'album,
    • la durée de l'album
    • sa taille (éventuellement)
    • les données audio qu'il contient

    La position n'est, effectivement - et bien que tu puisse, d'autre part, demander où le flux audio en est de la lecture - qu'une sorte de cache permettant de retrouver l'échantillon suivant à renvoyer.

  14. #14
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 282
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 282
    Points : 11 036
    Points
    11 036
    Par défaut
    La constance est une indication sémantique sur la donnée : savoir si une opération va modifier l'état visible de ton objet. Si dans les détails d'implémentation, il y a des choses qui sont altérées (cache et autres évaluations paresseuses, mutex, ...), ben ... ce sont des détails que l'appelant ne perçoit pas.

  15. #15
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par NiamorH Voir le message
    La signature de la méthode à implémenter est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Fill( char* data, size_t dataSize ) const.
    Le lecteur attentif aura remarqué le fameux const
    [...]
    Contexte moteur audio -> méthode Fill n'a qu'un seul but qui ne modifie pas l'objet
    Contexte lecteur audio -> méthode Fill a un autre but modifiant l'objet
    Désolé, mais comme deux appels à Fill n'ont pas le même effet -- on ne recoit pas les mêmes données -- , l'erreur pour moi est de mettre Fill comme membre const (à voir les membres de basic_istream, ses concepteurs ont le même avis).

    Un Fill qui pourrait être const aurait une interface comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Fill(size_t pos, unsigned char* data, size_t size) const;

  16. #16
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Points : 1 051
    Points
    1 051
    Par défaut
    Bien vu, j'aurais plutôt tendance à me ranger à cet avis.

    J'ai l'impression que les choses sont plus claires pour moi sur ce sujet maintenant.

    Merci pour vos interventions

  17. #17
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 382
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 382
    Points : 41 593
    Points
    41 593
    Par défaut
    C'est justement pourquoi dans le cas des conteneurs, les itérateurs sont des objets séparés, et non inclus dans la liste.

  18. #18
    Membre éprouvé
    Avatar de NiamorH
    Inscrit en
    Juin 2002
    Messages
    1 309
    Détails du profil
    Informations forums :
    Inscription : Juin 2002
    Messages : 1 309
    Points : 1 051
    Points
    1 051
    Par défaut
    Hum je suis pas sûr d'avoir pigé ta remarque Médinoc

  19. #19
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 382
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 382
    Points : 41 593
    Points
    41 593
    Par défaut
    Traduction:
    Quand tu parcoures une liste chaînée, tu ne stockes pas le pointeur "courant" dans la structure liste elle-même, mais tu le gardes à l'extérieur.
    Que ce soit sous la forme d'un itérateur (listes STL) ou autre chose (listes MFC).

  20. #20
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 282
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 282
    Points : 11 036
    Points
    11 036
    Par défaut
    Et aussi pour des histoires de ré-entrance et autres accès concurrents.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 14
    Dernier message: 21/11/2008, 17h29
  2. Méthodes virtuelle et implémentation
    Par slate dans le forum C++
    Réponses: 2
    Dernier message: 16/02/2006, 18h16
  3. méthodes virtuelles
    Par ep31 dans le forum C++
    Réponses: 2
    Dernier message: 09/11/2005, 18h21
  4. Comment l'appel à une méthode virtuelle....
    Par Blobette dans le forum C++
    Réponses: 7
    Dernier message: 07/12/2004, 14h55
  5. [C#] Méthode virtuelle
    Par jacma dans le forum Windows Forms
    Réponses: 4
    Dernier message: 07/11/2004, 09h18

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