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

Qt Discussion :

Pointeurs intelligents et doc (peut-être) incorrecte


Sujet :

Qt

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut Pointeurs intelligents et doc (peut-être) incorrecte
    Bonjour,

    Suite à une bonne dose de reflexion, il a été décidé que notre application ferait bien d'utiliser des pointeurs intelligents, afin de garantir que les données seraient bien là tant qu'on en a besoin, et bien détruites quand on ne s'en sert plus. Ces données étant potentiellement volumineuses, on s'est également dit qu'il serait bien qu'elles soient partagées tant qu'on ne les modifie pas.

    Les QSharedData, QSharedDataPointer, et QExplicitlySharedDataPointer semblent donc parfaitement adaptés à la tâche.

    N'ayant jamais programmé avec ces outils, j'ai codé rapidement des classes de test, afin de regarder le mode de fonctionnement. Et j'ai été surpris, en constatant que les QSharedPointer semblent se passer non pas un unique objet, mais des copies de cet objet.

    En gros, voici le test réalisé :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    QSharedDataPointer<TestSharedData> pa(new TestSharedData(a, QString("Objet Partage")));
    QSharedDataPointer<TestSharedData> pb = pa;
    QSharedDataPointer<TestSharedData> pc;
    pc = pa;
    Une affectation "normale", en fournissant au premier pointeur intelligent le pointeur vers l'objet à gérer (qui hérite de QSharedData, évidemment), en pa.
    Puis une affectation via le constructeur par copie, sur pb.
    Et enfin, une affectation via l'opérateur d'affectation, sur pc.

    En théorie, la documentation Qt dit que le constructeur par copie du QSharedDataPointeur devrait augmenter le compte de références, et assigner le pointeur sur l'objet partagé, rien de plus :
    QSharedDataPointer::QSharedDataPointer ( const QSharedDataPointer<T> & other )

    Sets the d pointer of this to the d pointer in other and increments the reference count of the shared data object.
    Et la même documentation dit aussi que l'opérateur d'affectation décrémente le compteur sur l'ancien objet partagé (en l'occurence, aucun objet), puis remplace le pointeur vers l'ancien objet par le pointeur vers le nouvel objet, et incrémente le compteur sur le nouveau :
    QSharedDataPointer<T> & QSharedDataPointer::operator= ( const QSharedDataPointer<T> & other )

    Sets the d pointer of this to the d pointer of other and increments the reference count of the shared data object. The reference count of the old shared data object of this is decremented. If the reference count of the old shared data object becomes 0, the old shared data object is deleted.
    Ces documentations semblent logiques.
    Sauf que, quand j'interroge l'adresse de l'objet partagé "détenu" par les pointeurs intelligents, je constate ceci :
    pa, après son initialisation, pointe sur un objet "partagé" (par lui seul), qu'on appelera A.
    pb, après son initialisation par copie de pa, pointe sur A. Très bien, ça. Sauf qu'après la même opération, si on regarde pa, lui, il pointe sur un objet B, qui est construit par copie de l'objet A.
    pc, après lui avoir affecté le pointeur pa, pointe sur l'objet B (le nouvel objet partagé par pa, si vous avez suivi). Sauf que pa, lui, pointe sur un objet C, obtenu par copie de l'objet B...

    La question que je me pose, évidemment, est de savoir pourquoi cette ]@[^\^[|##! de classe QSharedDataPointer recopie les données, au lieu de ne recopier QUE le pointeur.

    Je précise que la méthode de l'objet TestSharedData qui m'affiche l'adresse à laquelle se trouve l'instance courante est une méthode const, qui ne devrait donc pas déclencher la méthode detach()...

    Pour ceux que ça intéresse, le code est fourni en pièces jointes.
    La méthode "lanceTests(QObject *)" est celle utilisée à partir du main, en fournissant la QApplication en paramètre.

    Alors, petite précision :

    En ajoutant des tests, il semblerait que les affectations (par constructeur de copie, ou par opérateur d'affectation) se passent effectivement comme prévu, mais que les déréférencements utilisés pour visionner l'adresse de l'objet partagé déclenchent la copie systématiquement.

    Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    QSharedDataPointer<TestSharedData> pd(new TestSharedData(a, QString("D")));
    std::cout << "pd->x() = ##" << pd->x() << "##\n";
    QSharedDataPointer<TestSharedData> pe = pd;
    QSharedDataPointer<TestSharedData> pf = pd;
    std::cout << "pd->x() = ##" << pd->x() << "##\n";
    std::cout << "pe->x() = ##" << pe->x() << "##\n";
    std::cout << "pf->x() = ##" << pf->x() << "##\n";
    donne :
    Code x : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Constructeur de base appele : @@D@@
    pd->x() = ##D[08054300]##
    Constructeur de copie appele : ##D##
    pd->x() = ##Copie de D[0805db38]##
    Constructeur de copie appele : ##D##
    pe->x() = ##Copie de D[0805dbd8]##
    pf->x() = ##D[08054300]##

    Comme on peut le voir, c'est l'appel au déréférencement qui déclenche ça. (pas de copie le premier coup, puisque un seul pointeur sur l'objet partagé, puis on affecte l'objet à deux autres pointeurs, et on déréférence à nouveau. Cette fois, pd obtient une copie, laissant à pe et pf l'original. Puis on déréférence pe, qui obtient aussi une copie, laissant l'original à pf. Puis on déréférence pf, ce qui ne fait rien, car il est le dernier sur cette copie)

    Mais... pourquoi n'utilise-t-il pas le déréférencement constant, étant donné que la méthode x() est constante aussi ?
    Fichiers attachés Fichiers attachés

  2. #2
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut
    Autre mise à jour, dans un post séparé, tant pour des raisons de clarté que pour "bumper" le sujet :

    En fait, c'est un problème de "traduction" de méthode implicite, que je rencontre.

    Le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    QSharedDataPointer<TestSharedData> pg(new TestSharedData(a, QString("G")));
    std::cout << "pg->x() = ##" << pg->x() << "##\n";
    QSharedDataPointer<TestSharedData> ph = pg;
    QSharedDataPointer<TestSharedData> pi = pg;
    const TestSharedData *ppg = pg.operator const TestSharedData *();
    const TestSharedData *pph = ph.operator TestSharedData *();
    const TestSharedData *ppi = pi.operator const TestSharedData *();
    std::cout << "ppg = ##" << ppg->x() << "##\n";
    std::cout << "pph = ##" << pph->x() << "##\n";
    std::cout << "ppi = ##" << ppi->x() << "##\n";
    fournit le resultat suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Constructeur de base appele : @@G@@
    pg->x() = ##G[0805ecc8]##
    Constructeur de copie appele : ##G##
    ppg = ##G[0805ecc8]##
    pph = ##Copie de G[0805ed58]##
    ppi = ##G[0805ecc8]##
    On voit clairement, ici, que seul l'appel explicite à la méthode de déréférencement non-constante déclenche une copie, dans l'instance sur laquelle on l'a appelée. Donc, la classe fonctionne comme prévu.

    Par contre, le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    QSharedDataPointer<TestSharedData> pj(new TestSharedData(a, QString("G")));
    std::cout << "pj->x() = ##" << pj->x() << "##\n";
    QSharedDataPointer<TestSharedData> pk = pg;
    QSharedDataPointer<TestSharedData> pl = pg;
    std::cout << "--\n";
    const TestSharedData *ppj = (TestSharedData *) pj;
    std::cout << "--\n";
    const TestSharedData *ppk = (const TestSharedData *) pk;
    std::cout << "--\n";
    const TestSharedData *ppl = pl;
    std::cout << "--\n";
    std::cout << "ppj = ##" << ppj->x() << "##\n";
    std::cout << "ppk = ##" << ppk->x() << "##\n";
    std::cout << "ppl = ##" << ppl->x() << "##\n";
    fournit, lui, le résultat suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Constructeur de base appele : @@G@@
    pj->x() = ##G[0805edc8]##
    --
    --
    Constructeur de copie appele : ##G##
    --
    Constructeur de copie appele : ##G##
    --
    ppj = ##G[0805edc8]##
    ppk = ##Copie de G[0805eee0]##
    ppl = ##Copie de G[0805ef50]##
    Cette fois, on voit bien que le compilateur choisit, par défaut, la version non-constante (pl étant converti, implicitement, via une méthode qui copie l'objet, donc la méthode non-constante). Déja, cela me surprend, étant donné que la variable à laquelle on affecte le pointeur est un const T*, et pas un T* tout court.

    Je suis encore plus surpris de constater que les transtypages explicites, qui auraient pu (voire dû) aider le compilateur à choisir la bonne version, donnent au contraire un résultat contre-intuitif. En effet, (T*) pj semble appeler la version const du déréférencement, et (const T*) pk semble appeler, lui, la version non-constante. A n'y rien comprendre (en tout cas, pour quelqu'un qui, comme moi, n'est pas gourou C++).


    Ces éléments semblent-ils suffisants pour apporter une explication ?

  3. #3
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 035
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 035
    Par défaut
    Salut.

    Attention QSharedDataPointeur sert à implémenter le COW
    http://qt.developpez.com/faq/?page=g...es-memoire#COW

    Ce n'est pas plutôt QSharedPointeur que tu veut utiliser?http://qt.developpez.com/doc/latest/qsharedpointer.html

    Sinon :
    1- pourquoi revenir au pointeur? juste pour l'exemple?
    2- Peut tu mettre un code simple que l'on peut compiler? Un simple main par exemple. Histoire de voir ce que tu fait. Y as deux trois truc bizarres dans ce que tu montre.

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut
    Bonjour Yan,

    Non, ce n'est pas QSharedPointer que je veux, parce que certaines méthodes pourront nécessiter de faire une copie de l'objet (ce que QSharedPointer ne gère pas).

    Voici le code du main que j'utilise :
    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
    #include <iostream>
     
    #include <QtCore/QCoreApplication>
     
    #include "TestSharedData.h"
     
    int main(int argc, char *argv[])
    {
    	QCoreApplication a(argc, argv);
     
    	std::cout << "Debut des tests sur les QSharedData (implicites) :\n";
    	TestSharedData::lanceTests(&a);
    	std::cout << "Fin des tests sur les QSharedData (implicites) :\n";
     
    	return 0;//a.exec();
    }


    Sinon, pour la question "pourquoi revenir au pointeur", euh... pour executer des méthodes de l'objet partagé, non ? Mais si la question est "pourquoi affecter l'adresse déréférencée, dans l'exemple, à un pointeur normal ?", alors la réponse est : pour séparer l'opération de déréférencement de l'opération d'affichage. Et pour pouvoir tester différentes manières de "forcer" le déréférencement constant plutôt que le normal. Donc, oui, l'affectation à des pointeurs normaux est pour l'exemple.

    J'ai vu qu'il y avait des méthodes data() et constData() pour obtenir, explicitement, l'un ou l'autre pointeur, mais le souci est que l'appel est moins naturel. Dans un cas, ca fait comme un pointeur tout bête, alors que dans l'autre cas, c'est pa.data()->x(), donc moins agréable à lire et à écrire.




    Cela dit, je suis novice dans l'utilisation de ces classes, donc je ne sais pas si elles sont censées être utlisées de cette manière, ou autrement.

  5. #5
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 035
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 035
    Par défaut
    Citation Envoyé par kzwix Voir le message
    Voici le code du main que j'utilise :
    Et comment je fais pour compiler et tester sans le reste ^^
    Le but c'était de tester le code que tu as fait pour tes teste et voir s'il n'y as pas un truc qui cloche.

    Sinon, pour la question "pourquoi revenir au pointeur", euh... pour executer des méthodes de l'objet partagé, non ?
    Vue que c'est un pointeur inteligent, il faut plutôt utiliser l'operateur ->. Comme avec un pointeur .au lieu de
    tu peut faire

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut
    Citation Envoyé par yan Voir le message
    Et comment je fais pour compiler et tester sans le reste ^^
    Le but c'était de tester le code que tu as fait pour tes teste et voir s'il n'y as pas un truc qui cloche.
    Euh... en utilisant les fichiers .h et .cpp que j'ai joints au premier message posté dans ce sujet, peut-être ?

    Citation Envoyé par yan Voir le message
    Vue que c'est un pointeur inteligent, il faut plutôt utiliser l'operateur ->. Comme avec un pointeur .au lieu de
    tu peut faire
    Ok, c'est ce que j'utilise (regarde mes exemples, les affichages sont obtenus avec du pa->x() ). Le souci que j'ai, là, c'est qu'au lieu de choisir, intelligemment, le déréférencement constant pour ensuite utiliser x() (qui est une méthode const), il utilise le déréférencement "normal", lequel appelle detach(). C'est ce qui cause les copies qu'on peut constater dans les exemples.

    Un moyen simple pour éviter ce comportement idiot ?

  7. #7
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 035
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 035
    Par défaut
    Citation Envoyé par kzwix Voir le message
    Euh... en utilisant les fichiers .h et .cpp que j'ai joints au premier message posté dans ce sujet, peut-être ?
    sorry.

    Citation Envoyé par kzwix Voir le message
    Un moyen simple pour éviter ce comportement idiot ?
    rendre la fonction x() const ?
    Ce n'est pas un comportement idiot, mais normale. En c++, si tu ne dit pas au compilateur ce qui est const il ne va pas le deviner.

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut
    Attends, la fonction x() n'est-elle pas déja const, dans le code que j'ai envoyé ?

    Le souci que j'ai, là, c'est qu'au lieu de choisir, intelligemment, le déréférencement constant pour ensuite utiliser x() (qui est une méthode const), il utilise le déréférencement "normal", lequel appelle detach(). C'est ce qui cause les copies qu'on peut constater dans les exemples.

    Un moyen simple pour éviter ce comportement idiot ?

    Si la méthode est const, qu'il peut déréférencer en const, et qu'il déréférence en normal, je trouve que c'est idiot

    Cela dit, j'ai peut-être zappé un détail.

  9. #9
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 035
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 035
    Par défaut
    Citation Envoyé par kzwix Voir le message
    Attends, la fonction x() n'est-elle pas déja const, dans le code que j'ai envoyé ?
    ouai bon j'ai rien dit j'étais sur de mon idée.
    Je regarde ce que tu as fait.

  10. #10
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 035
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 035
    Par défaut
    Ok. J'ai refait le test :
    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
    48
    49
    50
    51
    52
    53
    #include <QtCore>
     
    class MyClass : public QSharedData
    {
        QString m_name;
    public :
        MyClass(const QString & name)
        {
            m_name = name;
            qDebug()<<"Construteur par defaut : " << this << " " << m_name;
     
        }
     
        MyClass(const MyClass & myClass)
        {
            m_name = myClass.m_name+"'";
            qDebug()<<"Construteur par recopie : " << this<< " " << m_name;
     
        }
        MyClass & operator=(const MyClass & myClass)
        {
            qDebug()<<"Operateur = : " << this<< " " << m_name << " << " << myClass.m_name ;
            m_name = myClass.m_name;
        }
     
     
        void display() const
        {
            qDebug()<<"display const  : "<< this<< " " << m_name; ;
        }
        void display()
        {
            qDebug()<<"display : "<< this<< " " << m_name; ;
        }
     
    };
     
     
    int main(int argc, char *argv[])
    {
       QSharedDataPointer<MyClass> p1( new MyClass("p1"));
       const QSharedDataPointer<MyClass> p2 = p1;
       const QSharedDataPointer<MyClass> p3 = p1;
       //P1,P2 et P3 partage l'instance
     
       //Recupère un pointeur et appel display => pas de const => copy. P2 et P3 partage l'instance.
       p1->display();
     
       //Recupère un pointeur const et appel display const => pas de copy. P2 et P3 partage toujours l'instance.
       p2->display();
     
        return 0;
    }
    Le problème est bien la différence entre l'appel de la fonction const et la fonction non const. Je ne connait pas la règle exacte et suis assez étonné comme toi que sur le cast en const T *, le compilateur ne prend pas celui qui correspondait.

    Pour l'opérateur ->, c'est différent, le compilateur ne peut pas choisir lequel est le mieux. Et par défaut il prend toujours la version non const.

    Tu devrais demander sur le forum C++, tu aura surement une réponse précise et le pourquoi. Y as peut être des moyen de faire préférer la fonction const avant la fonction non const quand c'est possible. Mais je crois qu'en C++ c'est deux fonctions distinctes sans aucune corrélation pour lui.

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut
    Ok, à priori, on m'a dit (sur le forum C++) que c'était parce que l'objet à partir duquel je déréférençais n'était pas const, qu'il utilisait cette version de l'opérateur.

    (http://www.developpez.net/forums/d95...e/#post5364473)

    En ce qui concerne Qt en lui-même, finalement, je ne pense pas avoir d'autre question sur ce sujet.



    Merci, en tout cas, pour avoir jeté un oeil

  12. #12
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 035
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 035
    Par défaut
    Citation Envoyé par kzwix Voir le message
    Merci, en tout cas, pour avoir jeté un oeil
    En tous cas j'ai réappris des trucs ^^.

  13. #13
    Responsable Qt & Livres


    Avatar de dourouc05
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2008
    Messages
    26 772
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2008
    Messages : 26 772
    Par défaut
    Citation Envoyé par kzwix Voir le message
    En ce qui concerne Qt en lui-même, finalement, je ne pense pas avoir d'autre question sur ce sujet.
    Dans ce cas, tu peux marquer le sujet comme , !
    Vous souhaitez participer aux rubriques Qt (tutoriels, FAQ, traductions) ou HPC ? Contactez-moi par MP.

    Créer des applications graphiques en Python avec PyQt5
    Créer des applications avec Qt 5.

    Pas de question d'ordre technique par MP !

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

Discussions similaires

  1. [...] doit utiliser une requête qui peut être mise à jour
    Par requiemforadream dans le forum ASP
    Réponses: 4
    Dernier message: 26/04/2005, 09h12
  2. Sepi peut-être sur SourceForge
    Par sjrd dans le forum Sepi
    Réponses: 2
    Dernier message: 06/04/2005, 10h41
  3. Réponses: 2
    Dernier message: 10/03/2004, 18h52
  4. question (peut-être idiote) sur les vues
    Par LadyArwen dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 26/03/2003, 10h35

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