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 :

interface et h�ritage multiple


Sujet :

C++

  1. #1
    Expert �minent

    Femme Profil pro
    Ing�nieur d�veloppement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    D�tails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (�le de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par d�faut interface et h�ritage multiple
    Bonjour � tous.
    J'ai un probl�me que je ne comprends pas et dont j'ai une solution qui ne me plait pas du tout.

    Soient trois d'interfaces avec impl�mentation par d�faut:
    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
     
    struct BaseSimple {
        virtual ~BaseSimple() = 0;
        virtual void simple() { cout << "BaseSimple" << endl; }
    };
     
    struct BaseRiche: public BaseSimple {
        virtual ~BaseRiche() = 0;
        virtual void riche() {}
    };
     
    struct Biduloide {
        virtual ~Biduloide() = 0;
        virtual void operator() () {}
    };
    Les trois destructeurs sont d�finis � l'abri des fichiers .cpp.

    Jusque l�, tout va bien, j'ai trois classes abstraites, dont un h�ritage.

    Viennent ensuite deux classes qui ne pose pas encore probl�me.
    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
    class Simple: public BaseSimple, public Biduloide {
    public:
        virtual void simple() override final {
            /* un peu d'intelligence */
            do_simple();
        }
    protected:
        virtual void do_simple() {}
    };
     
     
    class MoinsSimple: public Simple {
    protected:
        virtual void do_simple() override final { cout << "MoinsSimple" << endl;}
    };
    Si � pr�sent j'�cris ceci:
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    BaseSimple * s = new MoinsSimple();
    s->simple();
    J'esp�re bien voir s'afficher "MoinsSimple", puisque c'est ainsi que doit fonctionner les fonctions virtuelles.
    En effet, s->simple() est Simple::simple(), qui appelle Simple::do_simple(), virtuelle donc trouv�e dans MoinsSimple.

    Ajoutons une nouvelle classe:
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    class Riche: public BaseRiche, public MoinsSimple {
    public:
        using MoinsSimple::simple
    };
    D'apr�s le standard, j'ai le layout suivante: <Riche> = < <<BaseSimple> BaseRiche> <<<BaseSimple> Simple> MoinsSimple> >.
    Je vois bien que j'ai deux BaseSimple.
    J'ai ainsi trois d�finitions de simple(): deux dans les BaseSimple, et une dans Simple (cette derni�re version �tant marqu�e final)

    Si ma lecture �tait bonne, simple serait ambig�e dans Riche, donc j'ai �cris un using MoinsSimple::simple.
    Or, ce n'est pas le cas.
    Le code suivant affiche "BaseSimple"
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    BaseRiche * s = new Riche();//pour des raisons de smart_ptr à partager un peu partout
    s->simple();
    EDIT: dans mon vrai probl�me le pointeur n'est pas Riche* mais BaseRiche*

    J'ai bien une solution en rendant Simple::simple() non finale, et en �crivant:
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    class Riche: public BaseRiche, public MoinsSimple {
    public:
        virtual void simple() override final {MoinsSimple::simple();}
    };
    Mais je n'ai pas de raison de le faire, puisque c'est pr�cis�ment pour la rendre final que j'ai cr�� do_simple().

    Je pourrai peut-�tre m'en sortir avec de l'h�ritage virtuel, mais j'aimerai surtout comprendre pourquoi j'ai ce probl�me.

  2. #2
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    D�tails du profil
    Informations personnelles :
    Localisation : France, Paris (�le de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par d�faut
    Hello leternel

    Avec quel compilo cela pose-t-il probl�me ? Je viens de tester avec GCC 4.8, GCC 5.2, Clang 3.4, Clang 3.8 et tous se comportent bien de la mani�re que tu attends. Mais pas Visual Studio qui a le comportement que tu cites... A voir ce que �a donne sur la derni�re Preview, je n'ai pas encore pu tester.

    Je pense que tu as affaire � un bug de cl.exe

  3. #3
    Expert �minent

    Femme Profil pro
    Ing�nieur d�veloppement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    D�tails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (�le de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par d�faut
    En l'occurence, gcc version 4.8.2 20140120 (Red Hat 4.8.2-16) (GCC)

    Je suis all� un peu vite dans ma simplification de l'exemple.

    BaseRiche * s = new Riche(); s->simple(); produit "BaseSimple", avec ou sans using.
    Sans using, Riche * s = new Riche(); s->simple(); ne compile pas (ambigu), et fonctionne comme souhait� avc.

    La solution, si je ne veux pas lever le final, c'est l'h�ritage virtuel.

  4. #4
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    D�tails du profil
    Informations personnelles :
    Localisation : France, Paris (�le de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par d�faut
    C'est vraiment �tonnant comme comportement, je me demande ce qu'en dit la norme, mais il nous faudrait un avocat, c'est un peu subtil dans ce cas. C'est tr�s d�cevant

  5. #5
    Membre Expert
    Avatar de Pyramidev
    Homme Profil pro
    Tech Lead
    Inscrit en
    Avril 2016
    Messages
    1 513
    D�tails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyr�n�es)

    Informations professionnelles :
    Activit� : Tech Lead

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 513
    Par d�faut
    Bonjour,

    leternel, ton exemple est �quivalent � celui-ci, plus facile � lire :
    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
    37
    // Code compilé ici : http://coliru.stacked-crooked.com/
     
    #include <iostream>
     
    struct A
    {
        virtual void foo() { std::cout << "A::foo()\n"; }
    };
     
    struct B : public A
    {
        void foo() override { std::cout << "B::foo()\n"; }
        virtual void bobo() {}
    };
     
    struct C : public A
    {
        virtual void coco() {}
    };
     
    struct D : public B, public C
    {
        void         bobo() override {}
        void         coco() override {}
        virtual void dodo()          {}
    };
     
    int main()
    {
        std::cout << "Version de g++ : " << __VERSION__ << "\n\n"; // version 6.1.0
        D d;
    //  d.foo();                  // erreur de compilation : foo est ambigu
    //  static_cast<A&>(d).foo(); // erreur de compilation : A est une classe de base ambigüe
        static_cast<B&>(d).foo(); // appelle B::foo()
        static_cast<C&>(d).foo(); // appelle A::foo()
        return 0;
    }
    Dans mon exemple, � mon avis, sous le capot, �a se passe comme �a :
    Dans un objet de type D :
    • L'objet B contient un objet A qui contient un pointeur vers une table virtuelle dont les premi�res adresses de fonction sont :
      1. adresse de B::foo
      2. adresse de D::bobo
    • L'objet C contient un objet A qui contient un pointeur vers une table virtuelle dont les premi�res adresses de fonction sont :
      1. adresse de A::foo (et non pas B::foo)
      2. adresse de D::coco
    • Au moins une des deux tables pr�c�dentes a pour 3e adresse de fonction D::dodo.


    Dans l'exemple de leternel :
    • simple() a le r�le de foo.
    • BaseSimple a le r�le de A.
    • Simple a le r�le de B.
    • BaseRiche a le r�le de C.
    • Riche a le r�le de D.

  6. #6
    Expert �minent

    Femme Profil pro
    Ing�nieur d�veloppement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    D�tails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (�le de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par d�faut
    Je vais chercher dans la norme les explications sur les tables de virtuel, le layout des classes et l'h�ritage multiple.
    L'explication semble bonne.
    D'autant que rendre virtuel l'h�ritage de la classe de base (BaseSimple ou A) supprime le probl�me (� un using pr�s)

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

    Informations professionnelles :
    Activit� : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par d�faut
    Salut,

    L�, tu tombe dans le coup classique de l'h�ritage en losange / en diamant (de la mort)...

    Typiquement, si tu as la possibilit� de modifier les classes de base (BaseRiche et MoinsSimple), tu devrais en th�orie avoir recours � l'h�ritage virtuel afin qu'il n'y ait effectivement qu'une seule instance de BaseSimple, autrement, VS a bien raison, il me semble que tu es effectivement face � un UB.

    Ceci dit, si tu as effectivement la possibilit� de modifier tes classes de base, la v�ritable erreur est d'avoir fait en sorte que ce qui te sert d'interface h�rite de ta classe de base : tu devrais avoir des interfaces proche de
    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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    class ISimple{
    public:
        ISimple(ISimple const & ) = delete; // comme tout ce qui a sémantique d'entité, on ne peut pas la copier
        ISimple & operator= (ISimple const & ) = delete // comme tout ce qui a sémantique d'entité, on ne peut pas l'assigner 
        virtual void simple(){
            std::cout<<"simple"
        }
    protected:
        ISimple(/* paramètre éventuels*/); // comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
        ~ISimple(); // Comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
    private:
        /* contrairement à java, on pourrait très bien avoir des données membre ici... 
         * pour autant que l'on ai la (quasi) certitudes qu'elles seraient toujours utiles
         */
    };
    /* Et on fait bien sur pareil pour l'interface de Riche */
    class IRiche{
     
    public:
        IRiche(IRiche const & ) = delete; // comme tout ce qui a sémantique d'entité, on ne peut pas la copier
        IRiche & operator= (IRiche const & ) = delete // comme tout ce qui a sémantique d'entité, on ne peut pas l'assigner 
        virtual void riche(){
            std::cout<<"riche"
        }
    protected:
        IRiche(/* paramètre éventuels*/); // comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
        ~IRiche(); // Comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
    private:
        /* contrairement à java, on pourrait très bien avoir des données membre ici... 
         * pour autant que l'on ai la (quasi) certitudes qu'elles seraient toujours utiles
         */
    };
    /* et pour Biduloide */
    class IBiduloide{
    public:
        IBiduloide(IBiduloide const & ) = delete; // comme tout ce qui a sémantique d'entité, on ne peut pas la copier
        IBiduloide & operator= (IBiduloide const & ) = delete // comme tout ce qui a sémantique d'entité, on ne peut pas l'assigner 
        virtual void operator()(){
        }
    protected:
        IBiduloide(/* paramètre éventuels*/); // comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
        ~IBiduloide(); // Comme c'est une interface, on ne peut l'instancier qu'au travers des classes dérivées
    private:
        /* contrairement à java, on pourrait très bien avoir des données membre ici... 
         * pour autant que l'on ai la (quasi) certitudes qu'elles seraient toujours utiles
         */
    };
    Du coup, comme il n'y a plus aucun lien d'h�ritage entre ces trois classes, tu n'as plus aucun probl�me : tu peux cr�er une classe de base proche de
    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
    37
    38
    39
    40
    41
    42
     
    class BaseSimple : public ISimple{ /*la classe de base n'expose que l'interface ISimple */
        /* tu en fais ce que tu veux (redéfinir simple(), peut être?  mais rien ne t'y oblige !!! */
    };
    class BaseRiche : public BaseSimple, public IRiche{ /* Dans BaseRiche, on ajoute l'interface IRiche à l'interface ISimple*/
     
        /* tu en fais ce que tu veux (redéfinir simple() ou riche(), peut être?  mais rien ne t'y oblige !!!) */
    };
    class Simple : public BaseSimple, public IBiduloide{ /* dans Simple, on ajoute l'inerface IBiduloide à l'interface ISimple */
     
        /* tu en fais ce que tu veux (redéfinir simple() ou operator()(), peut être?  mais rien ne t'y oblige !!!)
    };
    class MoinsSimple : public Simple{ /* la classe MoinSimple ne fait que redéfinir les comportements de Simple */
     
    };
    /* Pour Riche, il faut faire un choix : 
     * SOLUTION N°1 : On veut la mélanger avec d'autres objets "connus comme étant" de type BaseSimple
     * (mais rien ne nous empêche de la transmettre à une fonction qui s'attend à recevoir un ISimple,  un IRiche ou un IBiduloide)
     */
    class Riche : public BaseSimple, public IBiduloide, public IRiche{
     
    };
    /* SOLUTION N°2 : on veut la mélanger avec des objets "connus comme étant" de type BaseRiche
     * (mais rien ne nous empêche de la transmettre à une fonction qui s'attend à recevoir un ISimple,  un IRiche ou un IBiduloide)
     */
    class Riche : public BaseRiche, public IBiduloide{
     
    };
    /* SOLUTION N°3 : on veut la mélanger avec des objets "connus comme étant" de type Simple
     * (mais rien ne nous empêche de la transmettre à une fonction qui s'attend à recevoir un ISimple,  un IRiche ou un IBiduloide)
     */
    class Riche : public Simple, public IRiche{
     
    };
    /* SOLUTION N°4 : on veut uniquement la mélanger avec des objets "connus comme éant" du type MoinsSimple
     * (mais rien ne nous empêche de la transmettre à une fonction qui s'attend à recevoir un ISimple,  un IRiche ou un IBiduloide)
     */
     
     */
    class Riche : public MoinsSimple, public IRiche{
     
    };
    En d�finitive, la seule question qu'il faudra te poser (et � laquelle il s'agira de donner une r�ponse) sera : "sous quelle forme (de "parent le plus d�riv�") est-ce que je veux que mes �l�ments de types Riche soient connus dans le meilleur des cas ", �tant donn� que, quoi qu'il advienne, il pourrons toujours �tre connus comme �tant de type BaseSimple, et il sera toujours possible de les transmettre � une fonction s'attendant � recevoir n'importe quelle partie de son interface
    [EDIT]
    Maintenant, si, pour une raison ou une autre, tu ne sais pas modifier tes classes de base (par exemple, parce qu'elles sont fournies par une biblioth�que tierce), il faut garder en m�moire que la pr�f�rence sera toujours donn�e � la composition et non � l'h�ritage (il faut d�j�, pour que l'h�ritage soit coh�rent, que LSP soit scrupuleusement respect�, et je subodore tr�s fortement que ce n'est pas le cas dans ton cas de figure )
    Tu pourrais donc tr�s bien avoir quelque chose comme (je reprends ici les termes de ton exemple d'origine)
    Code : S�lectionner tout - Visualiser dans une fen�tre � part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Riche : public MoinsSimple{
    public:
        /* on "remonte" le service manquant issu de BaseRiche */
       void riche(){
           riche_.riche();
       }
       /* et, pourquoi pas?, on peut permettre la récupération de la composante BaseRiche */
       BaseRiche /*const */ & toBaseRiche() /* const{
           return riche_;
       }
    private:
        BaseRiche riche_;
     
    }
    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

  8. #8
    Expert �minent

    Femme Profil pro
    Ing�nieur d�veloppement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    D�tails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (�le de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par d�faut
    Concr�tement, mon contexte fait que je peux faire ce que je veux, mais que j'ai explicitement besoin de:
    • une interface telle que ISimple
    • une interface telle que ISimple + IRiche
    • une impl�mentation g�n�rale de chacune, plus une impl�mentation sp�cifique de ISimple (pattern composite inside)


    En effet, je recr�e une partie d'une biblioth�que.
    Parmi ce que je dois garder, il y a une fonction devant prendre en argument un "EventHandler", ce "ISimple + IRiche".
    Le besoin m�tier est de pouvoir construire des ISimples basiques, ou compos�s de callbacks (des function<...>), et d'avoir un Riche qui propage des �v�nements.

    Ma conclusion du moment:
    Apr�s coup, je me dis qu'il n'est pas n�cessaire que la fonction simple() de ISimple+IRiche porte le m�me nom que simple() de ISimple.
    Le EventHandler pourrait n'�tre que compos� d'un ou plusieurs IRiche.

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

    Informations professionnelles :
    Activit� : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par d�faut
    Citation Envoy� par leternel Voir le message
    Concr�tement, mon contexte fait que je peux faire ce que je veux, mais que j'ai explicitement besoin de:
    • une interface telle que ISimple
    • une interface telle que ISimple + IRiche
    • une impl�mentation g�n�rale de chacune, plus une impl�mentation sp�cifique de ISimple (pattern composite inside)
    Ben, en C++, il n'y a absolument rien qui t'emp�che d'avoir une impl�mentation "par d�faut" pour tes interfaces... Tu peux d'ailleurs tr�s bien fournir des donn�es membres pour celles-ci, si certaines conditions sont respect�es (principalement : le fait que, quoi qu'il arrive, ces donn�es membres seront toujours utilis�es pour les diff�rentes d�rivations possibles )

    En effet, je recr�e une partie d'une biblioth�que.
    Parmi ce que je dois garder, il y a une fonction devant prendre en argument un "EventHandler", ce "ISimple + IRiche".
    Le besoin m�tier est de pouvoir construire des ISimples basiques, ou compos�s de callbacks (des function<...>), et d'avoir un Riche qui propage des �v�nements.
    Pourquoi ne passerais tu pas par un syst�me de signaux et de slots? Apr�s tout, les callbacks ne sont jamais qu'un moyen d'assurer une transmission "horizontale" des informations, et les signaux + slots correspondent effectivement � ce cas de figure
    Ma conclusion du moment:
    Apr�s coup, je me dis qu'il n'est pas n�cessaire que la fonction simple() de ISimple+IRiche porte le m�me nom que simple() de ISimple.e EventHandler pourrait n'�tre que compos� d'un ou plusieurs IRiche.
    Ce qui plaide en faveur de la composition par rapport � l'h�ritage, m�me si, d'une mani�re g�n�rale, il sera sans doute utile d'avoir "quelque part" une hi�rarchie regroupant ISimple et IRiche

    Si ce n'est cette hi�rarchie ne devrait -- � mon sens -- pas se faire au niveau des interface (IRiche h�ritant de ISimple), mais bien "parall�lement" � tes interfaces (dans le pire des cas, une classe de base dont h�rite une classe utilisant la composition de ISimple, une autre utilisant la composition de IRiche et une derni�re utilisant la composition des deux)
    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

  10. #10
    Expert �minent

    Femme Profil pro
    Ing�nieur d�veloppement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 202
    D�tails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (�le de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 202
    Par d�faut
    Je note toutes ces remarques, je mets tout ca en place et je ferai un retour.

+ R�pondre � la discussion
Cette discussion est r�solue.

Discussions similaires

  1. H�ritage virtuel et constructeur.
    Par Klaim dans le forum C++
    R�ponses: 17
    Dernier message: 10/01/2015, 21h18
  2. Interfaces VS h�ritage
    Par FRED.G dans le forum G�n�ral Dotnet
    R�ponses: 38
    Dernier message: 17/03/2007, 09h32
  3. [POO] Interface ou h�ritage ?
    Par s.n.a.f.u dans le forum VB.NET
    R�ponses: 3
    Dernier message: 17/03/2007, 01h02
  4. H�ritage Virtuel et SDL
    Par Qualimero dans le forum SDL
    R�ponses: 6
    Dernier message: 18/07/2006, 04h49
  5. Interface et h�ritage
    Par pirbd dans le forum Delphi
    R�ponses: 2
    Dernier message: 12/07/2006, 13h40

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