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 :

A propos de const


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut A propos de const
    Le mot clé const joue-t-il bien son rôle ?
    En général il est simple à comprendre et à utiliser, mais parfois il peut être très complexe. Par exemple dans la stl, c'est pas évident de s'y retrouver dans les iterator et les reference const et pas const (je parle de la lecture des sources pour "comprendre").

    Un exemple.
    J'ai une classe qui ne contient qu'un m_handle sur fichier et des méthodes simples qui appelent l'api de windows (ou autre...). Toutes les méthodes, sauf Open() et Close() (les seules qui modifient le membre m_handle), peuvent être const. Même Write() alors que le fichier est modifié. C'est logique me direz-vous, c'est une question de "concept".
    Supposont maintenant que l'on dérive cette classe pour y ajouter des membres comme m_seekposition, un buffer, un cache, etc. Bardaf, plus rien ne peut être const car même Read() modifie ces nouveaux membres. Et pour peu que les méthodes de la classe de base étaient virtuelles, on ne sait plus trop si c'était "bien" d'en faire des "const" au départ.

    Un autre exemple, plus syntaxique.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class I
    {
     friend class T;
     T * m_t;
     unsigned m_u;
    public:
     ...
     unsigned Get() const
      { return m_t->Get(m_u); }
     ...
    };
     
    class T
    {
     ...
     unsigned Get(unsigned p);
     ...
    };
    La méthode I::Get() est const mais T::Get() ne l'est pas. Ainsi le compilateur accepte d'appeler une méthode non const d'un de ses membres depuis une méthode const. C'est "curieux" je trouve. Et ça m'arrange bien d'ailleurs car parfois c'est une prise de tête pour savoir où mettre const ou pas (ce qui se résume finalement, souvent et malheureusement par n'en mettre nulle part quand on n'a pas trop le temps de réfléchir).

    Bref, comment faire pour bien implémenter "const" ?
    J'imagine que la réponse peut être vaste et sujette à de long débats...
    (et pour une fois, la réponse ne sera pas "boost" )

  2. #2
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    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 395
    Par défaut
    Ton dernier exemple met en évidence le fait que la constance ne soit pas propagée aux membres pointeurs d'une classe. C'est un fait connu, qui a ses détracteurs et ses supporters.

    Du côté du fichier, c'est avec ce genre d'abstraction qu'il faut faire la différence entre syntaxe et sémantique. Comme tu le fois, il y a des méthodes qui peuvent syntaxiquement être déclarées const sans que ça les empêche de compiler, mais qui modifient quand même le fichier. Ainsi, ces méthodes ne sont pas sémantiquement constantes et ne doivent donc pas être déclarées const.

    Pour les membres pointeurs d'un objet, un truc que j'aime bien, c'est ce code: Une classe de "pointeur bète" qui propage la constance.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    /*
    class const_dumb_ptr:
    	This class holds a pointer, but is NOT a smart pointer.
    	No destruction is performed on the pointer.
    	However, this class propagates const-ness to the pointer:
    	when a const_ptr object is const, it becomes a pointer to const object.
     
    	This class is best used for declaring class member variables, but remember
    	the containing class must not manage the lifetime of more than one resource
     
    This class is entirely implemented inline.
    */
    template< class T >
    class const_dumb_ptr
    {
    public:
    	typedef T elem_type;
    private:
    	elem_type * m_ptr;
    public:
    	//const_dumb_ptr() {} //No default constructor: Compiler will refuse uninitialized
    	const_dumb_ptr(elem_type *ptr) : m_ptr(ptr) {}
     
    	elem_type * operator-> ()
    	{ return m_ptr; }
    	elem_type const * operator-> () const
    	{ return m_ptr; }
     
    	operator elem_type* ()
    	{ return m_ptr; }
    	operator elem_type const* () const
    	{ return m_ptr; }
     
    	elem_type ** operator& ()
    	{ return &m_ptr; }
    };
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Perso, je suis un grand fan de const.
    J'ai malheureusement eu des cours de C++, ou le mot clé const n'était qu'une note en bas de page alors qu'à mon avis on devrait l'enseigner et l'utiliser dès le début.
    Trois avantages de const vraiment intéressant :

    1. Filet de protection. Il m'est arrive plusieurs fois qu'un const bien placé me sauve la mise, car je tentais de modifier un truc qu'il ne fallait VRAIMENT pas modifier.
    2. La constness se propage. Bon c'est un peu pénible parfois, mais au moins ça oblige à être rigoureux.
    3. Peut être plus important encore, grâce à const, l'intention du programmeur s'exprime directement dans le code, qui devient plus expressif.

    Un exemple réel. Dernièrement, j'ai trouvé une fonction qui ressemblait à celle-ci, dans du code qui n'utilisait pas du tout de const.
    Ben c'est pénible. Impossible de savoir ce qui se passe au juste en regardant directement le prototype. Qui update qui ? Il faut forcement inspecter le code source de A::Update() pour être sûr. Alors que la même fonction, mais cette fois trouvé dans du code respectant la constness prend tout son sens.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void A::Update(B& b);             // update mutuel de A et B
    void A::Update(B& b) const;       // update de B, grâce aux infos de A
    void A::Update(const B& b);       // update de A, grâce aux infos de B
    void A::Update(const B& b) const; // Probablement une erreur
    Supposont maintenant que l'on dérive cette classe pour y ajouter des membres comme m_seekposition, un buffer, un cache, etc. Bardaf, plus rien ne peut être const car même Read() modifie ces nouveaux membres.
    C'est presque mot pour mot l'exemple que l'on trouve dans les livres pour l'utilisation du mot clé mutable. Un membre déclaré mutable peut toujours être modifié que la fonction soit const ou non. Bon bien sur, il ne faut pas employer mutable à tort et à travers, mais typiquement, si sémantiquement la fonction doit être const, comme Open() ou Close() mais qu'un détail d'implémentation barre le chemin, comme m_seekposition, alors c'est un candidat parfait pour mutable.

    La méthode I::Get() est const mais T::Get() ne l'est pas. Ainsi le compilateur accepte d'appeler une méthode non const d'un de ses membres depuis une méthode const. C'est "curieux" je trouve. Et ça m'arrange bien d'ailleurs
    Bouh ! C'est mal !
    A chaque fois que tu passes par un pointeur pour contourner un const, saches que ton karma en prend un sacré coup.
    Bon plus sérieusement, j'ai eu une fois un débat avec un collègue pour savoir quoi faire dans ce genre de cas (La classe mère imposait un const visiblement abusif sur une fonction) On était tombé d'accord pour utiliser un const_cast histoire que la ruse saute aux yeux pour la prochaine personne qui va lire le code.

  4. #4
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    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 395
    Par défaut
    Franchement, je ne rendrais pas m_seekposition mutable, connaissant la sémantique habituelle des flux ou des handles de fichiers.

    Je suis plutôt du genre à rendre mutable des caches ou des compteurs de référence.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  5. #5
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    Ah oui, mutable. Je vois tellement rarement ce mot clé dans du code que j'en oubliais son existence...

  6. #6
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    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 395
    Par défaut
    Mais attention à ne pas en abuser. Pour moi, un truc comme seekPosition ne devrait pas être dans la classe Fichier, mais plutôt à l'extérieur, comme un itérateur.

    C'est le cas pour les fonctions d'E/S asynchrones de Windows: La structure OVERLAPPED contient l'offset où l'on souhaite lire, plutôt que se fier à l'offset interne du fichier. Cela permet plusieurs lectures simultanées.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    screetch
    Invité(e)
    Par défaut
    Medinoc a raison, que Read soit const est un gros problème de conception dans ce cas

    Une meilleure version de fichier serait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class File
    {
      File(const char *filename);
      ~File();
      iterator& begin();
      const_iterator& begin() const;
      iterator& end();
      const_iterator& end() const;
     
      class const_iterator
      {
        const File* target;
        int offset;
        const_iterator operator+(i64 offset);
        const_iterator& operator>>() // pour lire
      };
     
      class iterator
      {
        File* target;
        int offset;
        iterator operator+(i64 offset);
        iterator& operator>>() // pour lire
        iterator& operator<<() // pour ecrire
    };
    };
    ainsi, si tu recois un const File& tu ne pourras jamais ecrire dedans; si tu recois un File& tu pourras lire et ecrire
    Cette version la, avec des objets annexes, on se rend compte que le design fonctionne car le "const" est correct quoi qu'il arrive
    alors que dans ton premier message, tu montrais un probleme de conception puisque tu ne parvenais pas a faire un objet correctement "const".
    Dernière modification par screetch ; 19/02/2009 à 17h03.

Discussions similaires

  1. A propos de Last_insert_id
    Par f-demu01 dans le forum Administration
    Réponses: 2
    Dernier message: 26/03/2003, 08h32
  2. A propos depth buffer
    Par j.yves dans le forum DirectX
    Réponses: 1
    Dernier message: 03/12/2002, 00h41
  3. A propos des modèles d'objet (avec sources)
    Par DevX dans le forum C++Builder
    Réponses: 14
    Dernier message: 01/12/2002, 12h22
  4. Fonctionnement de la compression DivX
    Par Rodrigue dans le forum Algorithmes et structures de données
    Réponses: 2
    Dernier message: 20/09/2002, 14h10
  5. A propos du composant DBGrid
    Par _Rico_ dans le forum C++Builder
    Réponses: 2
    Dernier message: 24/07/2002, 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