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 :

Guru of the Week 104 [Trucs & Astuces]


Sujet :

C++

  1. #1
    Inactif  


    Homme Profil pro
    Inscrit en
    novembre 2008
    Messages
    5 295
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : novembre 2008
    Messages : 5 295
    Points : 15 671
    Points
    15 671
    Par défaut Guru of the Week 104
    C++ : une nouvelle question technique posée par Herb Sutter
    Qui relance les débats Guru of the Week suite à la sortie de C++11

    C'est une source d'information que les anciens connaissent bien. Guru of the Week (GotW) est un site créé et alimenté par Herb Sutter entre 1997 et 2003. Le principe est simple : une question technique est posée et les lecteurs interviennent pour répondre à la question en essayant de faire le tour de toutes les difficultés techniques qui pourraient apparaître. Cette discussion aboutie à une analyse en profondeur de la problématique posée. Ces questions et réponses ont eu tellement de succès que Herb Sutter a publié plusieurs ouvrages pour regrouper et compléter ces analyses : Exceptional C++ et More Exceptional C++.

    Ces ouvrages restent très intéressants à lire pour celui qui veut progresser en C++, mais avec la sortie du C++11, il commençait à devenir nécessaire de refaire le point en prenant en compte la nouvelle norme. Depuis peu, Herb Sutter a donc relancé les GotW sur son blog, Sutter’s Mill. La nouvelle série de questions a repris en commençant avec le numéro 100. Ce week-end, Herb Sutter a publié un nouveau GotW numéro 104. Voici un résumé de la problématique qui est soumise :

    Voici un code classique que l'on retrouve régulièrement dans beaucoup de projets.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    widget* load_widget( widget::id desired );
    Questions :
    • Qu'est-ce qui ne va pas avec le type de retour et que faudrait-il utiliser comme type de retour à la place ?
    • Vous ne souhaitez pas forcément modifier tout votre code existant pour être compatible avec le nouveau type de retour. Comment la compatibilité avec le code ancien sera-t-elle conservée ?


    Les solutions proposées doivent évidemment tenir compte de la nouvelle norme. Pour vous aider un peu, cette question est notée avec une difficulté de 5/10 et est dans la série des pointeurs intelligents.

    Sans aller voir les réponses proposées sur le site, sauriez-vous répondre à ces questions et être aussi bon que les lecteurs de Herb Sutter ?
    Connaissiez-vous les Guru of the Week et les avez-vous déjà lu ?

  2. #2
    Membre éprouvé
    Avatar de mitkl
    Homme Profil pro
    Étudiant
    Inscrit en
    février 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : février 2010
    Messages : 364
    Points : 1 198
    Points
    1 198
    Par défaut
    Connaissiez-vous les Guru of the Week et les avez-vous déjà lu ?

    Oui, depuis peu mais je n'ai jamais lu ceux qui précèdent le 100ème.


    Sans aller voir les réponses proposées sur le site, sauriez-vous répondre à ces questions et être aussi bon que les lecteurs de Herb Sutter ?

    Non pas directement mais de mémoire le 103ème faisait la distinction entre unique_ptr et shared_ptr et que le 104ème s'intitule "Smart Pointers part 2", la réponse doit donc être dans les pointeurs intelligents.
    Si vous ne savez toujours pas ce qu’est la récursivité, relisez cette phrase.

    Mon blog sur la programmation et l'informatique !

  3. #3
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 1 717
    Points : 3 285
    Points
    3 285
    Par défaut
    J'ai repondu hier directement sur le site.

  4. #4
    Inactif  


    Homme Profil pro
    Inscrit en
    novembre 2008
    Messages
    5 295
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : novembre 2008
    Messages : 5 295
    Points : 15 671
    Points
    15 671
    Par défaut
    Citation Envoyé par Klaim Voir le message
    J'ai repondu hier directement sur le site.
    Ton analyse est très intéressante. En voici un petit résumé pour ceux qui maîtrise pas l'anglais :

    Klaim pose la question de qui possède la responsabilité de la destruction de l'objet créé. Il distingue donc 3 possibilités :
    • la factory possède la responsabilité de détruire elle-même les objets qu'elle crée. Dans ce cas, un pointeur nu widget*, qui ne transmet aucune responsabilité, est indiqué.
    • la factory partage la responsabilité de la destruction de l'objet. Chaque utilisateur partage cette responsabilité et l'utilisation de shared_ptr<widget> est indiquée dans ce cas.
    • la factory n'a pour seule responsabilité que de créer l'objet, pas de le détruire. Elle donne donc la totalité de la responsabilité à l'utilisateur et il faut alors utiliser un unique_ptr<widget> dans ce cas.


    On voit ici qu'il y a plusieurs solutions à un même problème. La "bonne" solution peut donc dépendre des contraintes et besoins que l'on a. Une analyse de la responsabilité de la durée de vie de l'objet est intéressante pour optimiser l'utilisation des pointeurs intelligents.

    Que pensez-vous de l'intervention de Klaim et des questions qu'il soulève ?

    JolyLoic pose également une question intéressante. Il retourne le problème et fait la remarque suivante :

    Lorsque l'on rencontre un pointeur nu, il peut s'agir :
    • d'un objet dont la durée de vie est gérée par unique_ptr, le pointeur nu étant alors un simple pointeur sans responsabilité ;
    • d'une syntaxe ancienne, dont on n'a pas d'information sur qui possède la responsabilité de la durée de vie.

    On se retrouve alors à se poser la question suivante : doit-on détruire l'objet manuellement (second cas) ou laisser la responsabilité à d'autre (premier cas) ?
    JolyLoic propose donc d'ajouter un autre type de pointeur, qui prendrait clairement en charge le premier cas (donc un pointeur sans responsabilité pour un objet géré par un unique_ptr)

    Dans vos propres codes, comment distinguer vous les deux cas d'utilisation des pointeurs nus et éviter de détruire manuellement des objets gérés par unique_ptr ?

  5. #5
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    mai 2008
    Messages
    25 638
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : mai 2008
    Messages : 25 638
    Points : 200 212
    Points
    200 212
    Billets dans le blog
    80
    Par défaut
    Bonjour,

    Si la responsabilité n'est pas à nous de détruire le pointeur (cas de la factory, si j'ai suivi), alors il faudrait que le destructeur soit privé (ou protected )
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  6. #6
    Inactif  


    Homme Profil pro
    Inscrit en
    novembre 2008
    Messages
    5 295
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : novembre 2008
    Messages : 5 295
    Points : 15 671
    Points
    15 671
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Bonjour,

    Si la responsabilité n'est pas à nous de détruire le pointeur (cas de la factory, si j'ai suivi), alors il faudrait que le destructeur soit privé (ou protected )
    Sauf que widget* n'est pas une variable membre (d'ailleurs, load_widget n'a pas besoin d'être une classe ou une fonction membre), donc pas de notion de public ou private ici.
    Une petite révision du design pattern factory ? http://come-david.developpez.com/tut...e=Fabrique#LIV

  7. #7
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    mai 2008
    Messages
    25 638
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : mai 2008
    Messages : 25 638
    Points : 200 212
    Points
    200 212
    Billets dans le blog
    80
    Par défaut
    Bah, je parlais surtout d'une classe qui gérerai tous les widgets.
    Genre, vous charger le widget, le pointeur est gardé dans une "banque" et une copie du pointeur est renvoyé. Mais oui, j'imagine que l'on revient dans le cas de la variable membre ou similaire.
    Pour la Factory, je sais mais je ne voulais pas dire un mot interdit
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  8. #8
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 1 717
    Points : 3 285
    Points
    3 285
    Par défaut
    Merci gbdivers pour la traduction, j'avais pas le courage.

    Pour la solution de JolyLoic je pense qu'avoir un smart pointer qui indique clairement que l'objet est juste référencé (ref_ptr?) mais pas managé par l'utilisateur serait une aide surtout pour totalement suprimer la possibilité d'un delete sur le pointeur (hors .get() qui est facilement trouvable).

  9. #9
    Membre régulier
    Homme Profil pro
    Développeur .NET/C/C++
    Inscrit en
    septembre 2007
    Messages
    71
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur .NET/C/C++
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : septembre 2007
    Messages : 71
    Points : 114
    Points
    114
    Par défaut
    Bon ben voilà, j'ai répondu à mon tour sur le blog

    (En gros, mon idée est de créer une classe owner_ptr, laquelle se chargerai de garder l'ownership de la même manière que ce que fait un unique_ptr, mais qui autoriserait le partage de ce dernier via les weak_ptr.)
    "Toujours en faire plus pour en faire moins"

  10. #10
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : août 2004
    Messages : 1 717
    Points : 3 285
    Points
    3 285
    Par défaut
    Je ne vois pas bien la difference avec un shared_ptr. Et aussi ca serait super couteux par rapport a un pointeur qui ne fait qu empecher l utilisateur de faire un delete...

  11. #11
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    décembre 2008
    Messages
    830
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : décembre 2008
    Messages : 830
    Points : 2 604
    Points
    2 604
    Par défaut
    Je pense que le comité n'a pas été assez loin avec le unique_ptr.
    Je sais que weak_ptr n'existe que pour des raisons technique, mais une alternative pour le unique_ptr aurait été intéressante, pour moi.

    Pour en revenir a la question, je tiens tout de même a rappeler que d'utiliser des unique_ptr (shared_ptr aussi, bien que plus léger) possède un impact sur le code. Faible, mais existant.

    Par exemple, le constructeur par copie est supprimé.
    De même, il faut ensuite traquer les delete.
    Donc, si on ne veut pas modifier le reste du code, on ne peut pas utiliser les smart_ptr du C++ 11, mais en implémenter d'autres, selon la situation de responsabilité.

    Pour la 2nde question:
    j'étais déjà tombé sur les GotW, mais j'ai jamais pigé ce que signifiait cet acronyme ^^
    En tout cas, c'est une excellente chose que ça reprenne, je pense que je vais suivre ce truc, moi qui cherche a apprendre le C++ en profondeur ça ne peut que m'être bénéfique.

  12. #12
    Inactif  


    Homme Profil pro
    Inscrit en
    novembre 2008
    Messages
    5 295
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : novembre 2008
    Messages : 5 295
    Points : 15 671
    Points
    15 671
    Par défaut
    Citation Envoyé par Freem Voir le message
    Pour en revenir a la question, je tiens tout de même a rappeler que d'utiliser des unique_ptr (shared_ptr aussi, bien que plus léger) possède un impact sur le code. Faible, mais existant.
    Hum, j'aurais dit l'inverse. shared_ptr possède un compteur de référence, contrairement à unique_ptr et est donc plus "gros" (voir le GotW 102 pour une représentation graphique de la mémoire). Pour moi, unique_ptr n'a pas de surcoût (mémoire ou temps d'accès) par rapport à un pointeur nu.

    Citation Envoyé par Freem Voir le message
    Par exemple, le constructeur par copie est supprimé.
    Attention, unique_ptr n'interdit pas d'avoir autant de pointeur que l'on veut sur un objet. On peut récupérer des pointeurs nus avec get(). Il dit juste "c'est moi qui ait la responsabilité de détruire l'objet".

    Citation Envoyé par Freem Voir le message
    De même, il faut ensuite traquer les delete.

    Donc, si on ne veut pas modifier le reste du code, on ne peut pas utiliser les smart_ptr du C++ 11, mais en implémenter d'autres, selon la situation de responsabilité.
    Comme indiqué par l'un des intervenants, il sera quand même plus simple de faire une recherche (même sur un gros projet) pour supprimer les delete que de recréer des classes de pointeurs intelligents (sauf peut être le pointeur intelligent proposé par JolyLoic)

    Donc, en pratique, on peut modifier un code existante de la façon suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    A* a = new A();
    // utilisation dans tous les sens de a et pleins de copies de a
    delete a; // ou delete une des copies de a ? Le problème est là...
    Par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    unique_ptr<A> a_owner = unique_ptr<A>(new A()); // toujours pas de make_unique...
    A* a = a_owner.get();
    // utilisation dans tous les sens de a et pleins de copies de a
    // delete a; on supprime les deletes

  13. #13
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    décembre 2008
    Messages
    830
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : décembre 2008
    Messages : 830
    Points : 2 604
    Points
    2 604
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    Hum, j'aurais dit l'inverse. shared_ptr possède un compteur de référence, contrairement à unique_ptr et est donc plus "gros" (voir le GotW 102 pour une représentation graphique de la mémoire). Pour moi, unique_ptr n'a pas de surcoût (mémoire ou temps d'accès) par rapport à un pointeur nu.
    Tu m'as mal compris. Je parlais d'impact sur le code, pas sur la performance.
    Shared_ptr possède par exemple un constructeur par copie, et donc peut être passé par valeur, par exemple.
    Citation Envoyé par gbdivers Voir le message
    Attention, unique_ptr n'interdit pas d'avoir autant de pointeur que l'on veut sur un objet. On peut récupérer des pointeurs nus avec get(). Il dit juste "c'est moi qui ait la responsabilité de détruire l'objet".
    C'est vrai, mais l'utilisation de pointeur nu crée un risque de delete qui rend le pointeur unique un peu fragile, de mon point de vue. Ce qui ne m'empêche pas d'utiliser unique_ptr quand je le peux.
    Citation Envoyé par gbdivers Voir le message
    Comme indiqué par l'un des intervenants, il sera quand même plus simple de faire une recherche (même sur un gros projet) pour supprimer les delete que de recréer des classes de pointeurs intelligents (sauf peut être le pointeur intelligent proposé par JolyLoic)

    Donc, en pratique, on peut modifier un code existante de la façon suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    A* a = new A();
    // utilisation dans tous les sens de a et pleins de copies de a
    delete a; // ou delete une des copies de a ? Le problème est là...
    Par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    unique_ptr<A> a_owner = unique_ptr<A>(new A()); // toujours pas de make_unique...
    A* a = a_owner.get();
    // utilisation dans tous les sens de a et pleins de copies de a
    // delete a; on supprime les deletes
    Ce fameux make_unique serait effectivement intéressant.

    Dans le code remplacement, il reste que si il y a eu transfert de responsabilité par copie dans une branche du code mais pas toutes, on s'expose à des delete multiples sur une même adresse mémoire.
    Bien sûr, on devrait remettre un pointeur a nul quand on transfère sa responsabilité a quelqu'un d'autre, mais ce qui devrait être fait ne l'est pas toujours.
    Par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    A *a=new A();
    if(cnd==true)
    {
      A *b=a;
      myFunc(b);
    }
    else
      delete a;
    return;
    Bien sûr, ce code n'est pas propre, a aurait dû être remis à 0 lors du transfert. Bien que d'un point de vue optimisation, avant C++11 et la move semantic, ça aurait été un gâchis de ressource processeur selon mes faibles connaissances (il y a peut-être une alternative a std::swap que je ne connaît pas).
    Mais on a pas la garantie qu'il n'existe pas, dans un code qui n'est pas de soi-même. Si on fait un simple "trouver/remplacer" par exemple, on s'expose à un crash, car unique_ptr nettoiera la mémoire qui aura été nettoyée par myFunc.
    Donc, remplacer un pointeur ancien par un unique_ptr est une opération à ne pas prendre à la légère pour moi, et je ne m'y risquerais pas sans contrôler tous les usages du code que j'ai altéré.

  14. #14
    Membre régulier
    Homme Profil pro
    Développeur .NET/C/C++
    Inscrit en
    septembre 2007
    Messages
    71
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur .NET/C/C++
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : septembre 2007
    Messages : 71
    Points : 114
    Points
    114
    Par défaut
    Citation Envoyé par Klaim Voir le message
    Je ne vois pas bien la difference avec un shared_ptr. Et aussi ca serait super couteux par rapport a un pointeur qui ne fait qu empecher l utilisateur de faire un delete...
    Par rapport à un shared_ptr, on n'a pas de compteur pour les réferences fortes. De même, un owner_ptr n'aurait pas de constructeur de copie (juste un move constructeur)
    Sinon, au niveau du cout ce serait effectivement plus couteux qu'un simple unique_ptr, mais plus léger que des shared_ptr.
    "Toujours en faire plus pour en faire moins"

  15. #15
    Membre à l'essai
    Profil pro
    Inscrit en
    février 2008
    Messages
    9
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : février 2008
    Messages : 9
    Points : 14
    Points
    14
    Par défaut Aye! Cela fait mal à un vieux dépassé comme moi
    Pour ma part, je n'ai jamais embarqué aussi profondément dans ces aspects sombres et techniques ( alien :-) ) du C++ ! Je suis un vieux codeux en C, qui adore le C++ mais piégé dans l'ancien mode permissif potentiellement chaotique du C. C'est tout juste si je comprend la surface des templates template <typename T> class A{....};. Alors 'a' dans A* a = new A(...);. Je sais que je suis responsable de la durée de vie de 'a' - un point c'est tout...
    C'est qui le programmeur maintenant ? le langage ? ou le codeux ?

  16. #16
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    @xbretzel: Et quand tu fais
    Ce n'est pas le langage qui s'occupe de a ? Meme en C ? D'ailleurs dans
    Tu n'es pas responsable de a au sens propre, mais de l'objet pointé par a, cad *a.

    Quand on parle de responabilité de la durée de vie, la question n'est pas de savoir si c'est le langage ou le programmeur qui s'occupe de ca, mais surtout où le faire ?

    Ensuite en C++ on utilise des capsules RAII qui transfert ca au langage, c'est nécessaire en particulier à cause des exceptions. En C il n'y a pas d'excpetion, cependant la gestion propre des erreurs et des allocations dynamique n'en est pas plus simple (goto pour faire du RAII entre autre).

    Ensuite vient une question de "verbosité" que se pose Loic Joly. Le langage nous permet d'écrire différentes capsule (*) qui indique clairement si l'objet doit être détruit par l'appelant, si la fonction qui l'a crée s'occupe déjà de le détruire (**) ou enfin si ils ont besoin de pouvoir le détruire les deux. Ainsi Loic remarque qu'il manque une capsule pour dire : "la fonction s'occupe déjà de le détruire" (***).

    (*) Si il existe assez de capsule, alors le besoin de pointeur nue dans le code métier peut totalement disparaitre. De même que le besoin de new/delete (toujours dans le code métier) avec les fonctions de création (make_shared,make_unique).

    (**) Dans le cas où il peut ne pas y avoir d'objet retourné, sinon une référence me semble plus indiqué. D'ailleurs je pense qu'en terme d'utilisation, std::unique_ptr et les références doivent passer devant std::shared_ptr et une éventuelle nouvelle capsule.

    (***) Dans un monde idéal sans se préoccuper "d'ancien code" ca ne serait pas nécessaire : pointeur nue = pas de responsabilité.

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