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++

  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.

  8. #8
    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
    File const * target, non?
    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.

  9. #9
    screetch
    Invité(e)
    Par défaut
    oué :p

  10. #10
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    screetch, je ne comprends pas ton code.
    Pourquoi const_iterator& end(); et non pas const_iterator& end() const; dans File ?

    Pourquoi il y a File * target dans const_iterator et pas dans iterator ?

    Médinoc,
    Pourquoi const File * target ? Dans ce cas un hypothétique target->Read(buffer,size) dans l'operator>> serait refusé.

  11. #11
    Expert confirmé

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Billets dans le blog
    3
    Par défaut
    C'est vraiment une question de sémantique....

    Présentons le dans ce sens:
    Si une fonction Foo() d'un objet Bar est constante, alors, appeler plusieurs fois Bar::Foo() successivement doit retourner toujours le même résultat (ou faire toujours la même chose).

    Dans le cas d'un File::Read() (ou File::Open()) ce n'est évidemment pas le cas (même si on ne conserve pas le seekPosition, l'OS va le faire 'behind the scene')... et appeler plusieurs fois la même fonction ne retournera pas le même résultat. Utiliser const est donc une erreur dans ce cas...

    Par contre, un File::Size() qui va lire la taille du fichier doit être const. Meme si on stocke la taille dans un 'cache' (qui lui pourra être mutable).

  12. #12
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    Voici un code très rapidement tapé.
    L'info retournée par DataLoader::SearchData(key) ne dépend que de key et donc doit être const. Mais l'usage interne de m_data et m_index m'oblige à en faire des mutable. Y-a-t-il mieux ?
    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
    class File
    {
     ...
     size_t Read(void *, size_t);
     ...
    };
     
    class DataLoader
    {
     mutable File m_data;
     mutable File m_index;
     ...
     bool SearchData(unsigned key) const
     {
       ...
       seek=m_index.ReadFromKey(key);
       ...
       m_data.Seek(seek.offset,file_begin);
       return m_data.Read(buffer,seek.len);
     }
    };

  13. #13
    screetch
    Invité(e)
    Par défaut
    j'ai édité... c'est pas du code c++ qui compile, forcément, c'etait pour montrer que si on arrive pas a garder le "const" sur certaines opérations, c'est sans doute que le design est mal fait.

  14. #14
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    Ok, c'est plus consistant, merci.
    Mais je ne suis pas d'accord avec const File* target dans const_iterator. Un target->Read() va échouer (car le Read n'est pas const).

    Par ailleurs, suppose une fonction de ce genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    data LoadData(const_iterator & it)
    {
     data d;
     target >> d;
     return d;
    }
    Je préfèrerais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    data LoadData(const const_iterator & it)
    {
     data d;
     target >> d;//ici, compilateur pas d'accord !
     return d;
    }
    Bardaf !

    Bon, ok, on va faire comme la STL, passer les itérateurs par valeur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    data LoadData(const_iterator it)
    {
     data d;
     target >> d;
     return d;
    }
    Et là on rejoint une autre discussion... celle où il est admis qu'en général il convient de passer par reference plutôt que par valeur...

    Bref on finit par se mordre la queue si vous voyez ce que je veux dire.
    Pas évident de faire le bon choix sémantique et/ou conceptuel dès le départ.

  15. #15
    Membre Expert
    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
    Par défaut
    L'info retournée par DataLoader::SearchData(key) ne dépend que de key et donc doit être const
    Ça c'est faux comme postulat.

    const, ça veut dire que à la fin de l'appel de ta fonction, l'objet est dans le même état qu'au départ. C'est à dire que toute opération possible sur l'objet, au travers de son interface publique (ce qui inclut les membres protected) aura exactement le même comportement après cet appel, qu'avant l'appel.

    Soit ta routine respecte ceci, et elle doit être const, soit elle ne le respecte pas, et elle ne doit pas l'être.

  16. #16
    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
    Avec un File défini ainsi, le comportement de SearchData() varie en effet, donc elle ne doit pas être const.

    Mais en fait, avec une telle définition, le File devrait plutôt être appelé Stream.

    Si on voulait une classe File, il faudrait un truc de ce genre:
    Code C++ : 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
    //Note: This class is swappable, but noncopyable.
    class File
    {
    private:
    	int posixDesc;
    	bool allowWrite;
    public:
    	typedef size_t offset_type;
     
    	File();
    	//Throws if open() fails.
    	File(const char *name, bool allowWrite=false);
    	~File();
    	void Swap(File &);
     
    	//Throws if an error occurs, but not if end of file
    	size_t read(offset_type fileOffset, void *buffer, size_t length) const;
    	//Throws if an error occurs.
    	void write(offset_type fileOffset, void const *data, size_t length);
    	//Throws if an error occurs.
    	size_t getLength() const;
    private:
    	File(const File &);
    	File& operator= (File);
    };
    Ce code implique l'utilisation de lseek() dans chaque fonction, restreignant la classe aux vrais fichiers et l'interdisant aux tubes. Ensuite, rien n'empêche de créer une classe abstraite Stream, et une implémentation FileStream qui lirait les données sur le fichier...
    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.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 636
    Par défaut
    salut,

    Je revient sur
    Citation Envoyé par camboui Voir le message
    <snip>
    Je préfèrerais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    data LoadData(const const_iterator & it)
    {
     data d;
     target >> d;//ici, compilateur pas d'accord !
     return d;
    }
    Bardaf !
    Car là, tu fais preuve d'une mauvaise compréhension de ce que peut être un itérateur...

    En effet, un itérateur est fait pour... itérer sur des éléments

    Si l'itérateur venait à être constant, il ne pourrait pas... passer au suivant (ou au précédent)

    Le but d'un const_iterator est... de s'assurer que l'objet manipulé par l'élément ne sera pas modifié, et c'est ce à quoi veille la classe const_dump_ptr fournie par Medinoc
    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

  18. #18
    Membre éclairé
    Inscrit en
    Avril 2005
    Messages
    1 110
    Détails du profil
    Informations forums :
    Inscription : Avril 2005
    Messages : 1 110
    Par défaut
    Euh... ce n'est pas moi qui ai introduit l'iterator dans cette discussion.
    Je n'ai fait que rassembler certaines idées d'intervenants. Faut lire les posts dans le contexte de la discussion...

    Et puis, à propos d'iterator, je ne suis pas sûr qu'il soit décidé comment les passer en paramètre, par valeur ou par const reference.

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