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 :

Retourner un pointeur


Sujet :

C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 57
    Points : 53
    Points
    53
    Par défaut Retourner un pointeur
    Suite a ma question existentielle ici, je ne vois pas
    pourquoi ceci en C++ ?

    Example 65 Dangerous memory management

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    String myFunc( const char* myArgument )
       {
           String* temp = new String( myArgument );
           return *temp;
           // temp is never deallocated and the user of myFunc
           // cannot deallocate because a temporary copy of
           // that instance is returned.
       }
    Que faire alors ?

  2. #2
    Membre averti
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    399
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 399
    Points : 413
    Points
    413
    Par défaut
    Je ne dirais meme pas que c'est une gestion de la mémoire dangereuse mais tout simplement que c'est un exemple qui présente une fuite mémoire. Car tu retournes en effet une copie de la string dynamiquement alloué dans la fonction. Cette copie n'a pas la même adresse (elle est sur la pile). Tu perds donc l'adresse de la string originale allouée dynamiquement sur le tas et tu ne pourra jamais la libérer -> conséquence : de la mémoire perdue a chaque appelle de la fonction.

    Pour éviter cela, il faut retourner un pointeur, mais c'est a utiliser délicatement également car la c'est une gestion de la mémoire pontentiellement dangereuse car l'utilisateur doit alors désallouer de la mémoire qu'il n'a pas lui même alloué. Bref il faut savoir ce qu'on fait.

    Mais la bonne méthode est de ne pas allouer la mémoire dynamiquement.

    Quand tu dis que faire ? C'est dans quel sens ? En regle général il faut éviter au maximum l'allocation dynamique.
    SPARK
    Moteur de particule C++ opensource avec modules de rendu OpenGL, Irrlicht et SFML

  3. #3
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
       String myFunc( const char* myArgument )
       {
           return String(myArgument);
       }
    Tout simplement.
    Le C++ c'est pas du Java, on met pas des new partout.
    Boost ftw

  4. #4
    Membre actif
    Profil pro
    Inscrit en
    Mars 2008
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2008
    Messages : 152
    Points : 209
    Points
    209
    Par défaut
    Pour répondre à ta question il faut rappeller les bases de l'utilisation d'un pointeur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    ...
        Bar *pFoo = new Bar; // Alloue un espace mémoire
        ...
        delete pFoo; // Libère l'espace mémoire
        pFoo = 0; // Pour plus de sécurité, mais non indispensable
        ...
    Or dans ta fonction tu ne peux pas faire de "delete temp". Par conséquent, l'espace mémoire ne sera libérée qu'à la fin de l'exécution du programme.

    Ensuite, je me pose la question de l'utilité d'une telle pratique. Il faut vraiment avoir l'esprit tordu.
    Contrairement au titre de ton sujet, la fonction "myFunc" ne retourne pas un pointeur (plutôt l'adresse vers laquelle pointe le pointeur), mais la valeur vers laquelle pointe l'adresse contenu dans "temp".

    En gros c'est la même chose, en beaucoup moins bien que:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    const char myArgument = 'a';
    String foo (myArgument);
    Quoiqu'il en soit, en C++ on n'utilise essentiellement les références.
    On utilisera un pointeur uniquement si on ne peut pas faire autrement, ce qui est très rare.

    Pour le reste Frifron a répondu. Si tu veux pouvoir faire un delete sur le pointeur, il faut que la fonction "le retourne", mais c'est vraiment une très mauvaise pratique.

    Si je dois résumer. Le principe à appliquer est simple.
    Une fonction qui déclare un pointeur doit obligatoirement le détruire.
    @Ikey

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 57
    Points : 53
    Points
    53
    Par défaut
    Merci pour ces premieres precisions.

    L'exemple que j'ai pris etait un exemple tire d'une liste de recommandations. Mais ils se rapprochait de ma question existentielle version C++.

    Disons que ca ne soit pas un String (qui est deja tres evolue) mais juste un tableau d'entiers. Je sais aussi que tout compte fait, mieux vaudrait passer par un vector et laisser a vector tous ces details sanglants.

    Mais pour aller au fond des choses, supposons que je veux fournir a un utilisateur un tableau des n premiers nombres entiers ..

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    int * AllocateAndPopulatePrimes(const int n){
       int *temp= new int[n];
     
       //// ici je remplis temp
     
      return temp;
    }
    Ou sont les pb ?

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 57
    Points : 53
    Points
    53
    Par défaut
    Citation Envoyé par Frifron Voir le message
    Je ne dirais meme pas que c'est une gestion de la mémoire dangereuse mais tout simplement que c'est un exemple qui présente une fuite mémoire. Car tu retournes en effet une copie de la string dynamiquement alloué dans la fonction. Cette copie n'a pas la même adresse (elle est sur la pile). Tu perds donc l'adresse de la string originale allouée dynamiquement sur le tas et tu ne pourra jamais la libérer -> conséquence : de la mémoire perdue a chaque appelle de la fonction.
    Excuse-moi, qui est sur le tas et qui est sur la pile ?

  7. #7
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Citation Envoyé par mailaka Voir le message
    Mais pour aller au fond des choses, supposons que je veux fournir a un utilisateur un tableau des n premiers nombres entiers ..

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    int * AllocateAndPopulatePrimes(const int n){
       int *temp= new int[n];
     
       //// ici je remplis temp
     
      return temp;
    }
    Ou sont les pb ?
    Ici, il n'y a aucun problème. Il y en aurait un si le tableau était directement une variable locale, mais c'est impossible car n n'est pas une constante connue à la compilation.
    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.

  8. #8
    Membre actif
    Profil pro
    Inscrit en
    Mars 2008
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2008
    Messages : 152
    Points : 209
    Points
    209
    Par défaut
    Citation Envoyé par mailaka Voir le message
    Merci pour ces premieres precisions.

    L'exemple que j'ai pris etait un exemple tire d'une liste de recommandations. Mais ils se rapprochait de ma question existentielle version C++.

    Disons que ca ne soit pas un String (qui est deja tres evolue) mais juste un tableau d'entiers. Je sais aussi que tout compte fait, mieux vaudrait passer par un vector et laisser a vector tous ces details sanglants.

    Mais pour aller au fond des choses, supposons que je veux fournir a un utilisateur un tableau des n premiers nombres entiers ..

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    int * AllocateAndPopulatePrimes(const int n){
       int *temp= new int[n];
     
       //// ici je remplis temp
     
      return temp;
    }
    Ou sont les pb ?
    J'ai déjà répondu à la question:
    Citation Envoyé par @Ikey Voir le message
    Une fonction qui déclare un pointeur doit obligatoirement le détruire.
    Or là tu déclares le pointeur "temp" et tu laisses à qui le récupère la tache de le détruire! [Pas bien!!]



    @Ikey

  9. #9
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    @@Ikey: J'espère que c'est une plaisanterie.
    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.

  10. #10
    Membre actif
    Profil pro
    Inscrit en
    Mars 2008
    Messages
    152
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2008
    Messages : 152
    Points : 209
    Points
    209
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    @@Ikey: J'espère que c'est une plaisanterie.
    Autant pour moi, je me suis très mal exprimé. Le pointeur est biensûr détruit à la fin de la méthode.
    Je voulais parler de l'espace mémoire allouée à "int[n];" dans le free store qui n'est pas libéré.

    @Ikey

  11. #11
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Et en plus il est sérieux...

    Leçon du jour: Les fonctions retournant un pointeur vers une mémoire nouvellement allouée.

    C'est une façon très courante de retourner un tableau. La fonction alloue le tableau avec new[], et la responsabilité de sa suppression revient à la fonction appelante, qui doit faire un delete[] une fois que la mémoire allouée n'est plus nécessaire.
    En C++, il est conseillé d'utiliser des pointeurs intelligents (comme des auto_ptr<>, ou les shared_ptr<> de Boost) afin d'éviter les fuites de mémoire si la fonction appelante oublie de désallouer.
    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.

  12. #12
    Membre éclairé

    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    717
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 717
    Points : 858
    Points
    858
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    C'est une façon très courante de retourner un tableau.
    En C, peut-être, pas en C++.

    En C++, il est conseillé d'utiliser des pointeurs intelligents (comme des auto_ptr<>, ou les shared_ptr<> de Boost) afin d'éviter les fuites de mémoire si la fonction appelante oublie de désallouer.
    Il est surtout conseillé de ne pas utiliser new[] au profit, notamment, de std::vector.

  13. #13
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Ça dépend des besoins en performance, car toutes les implémentations de vector ne font pas de copy-on-write. Dupliquer tout le vecteur n'est pas toujours la meilleure solution...
    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.

  14. #14
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Où est-ce que tu dupliques un vecteur ?
    (Aucune implémentation ne fait de COW sur les vecteurs à ma connaissance)
    Boost ftw

  15. #15
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Très simple: Si tu retournes un vecteur par valeur, son constructeur de copie est appelé (sauf peut-être en cas de RVO).

    Mais j'ai un meilleur exemple de fonction retournant un pointeur nouvellement alloué: Le pattern Factory...
    Ou celui de la méthode clone()...
    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.

  16. #16
    Membre averti
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    399
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 399
    Points : 413
    Points
    413
    Par défaut
    Mais on n'a pas dit qu'il ne fallait jamais utiliser de fonction allouant de la mémoire et retournant le pointeur. On a juste dit qu'il fallait l'utiliser en faisant attention car cause possible de fuite mémoire. Et que très souvent l'allocation statique peut être utilisé de facon beacoup plus sûre et performante.
    SPARK
    Moteur de particule C++ opensource avec modules de rendu OpenGL, Irrlicht et SFML

  17. #17
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 57
    Points : 53
    Points
    53
    Par défaut
    Ok, je pense que l'utilisation de vector est conseillee generalement. Pour le deuxieme exemple de mon cru, effectivement, ca laisse la responsabilite de la release de la memoire a l'utilisateur de la fonction. Mais bon, c'est la vie

    Sinon, comment se passerait l'utilisation de pointeur intelligent pour alleger la memoire .. oops le travail de l'utilisateur ? Quand utiliser auto_ptr<> au lieu de shared_ptr<> ?

    PS1 : Frifron, pourrais-tu mieux expliciter ton premier post, stp ? Je ne vois pas tres bien ou se situe la fuite memoire en fait sur l'exemple en string.

    PS2 : Medinoc, c'est quoi un RVO ?

  18. #18
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Pour auto_ptr<>, je ne sais pas trop, je dois avouer que je ne l'utilise jamais...

    La fuite mémoire du premier post: Tu alloues une String sur le tas, mais tu retournes par valeur une copie de cette String. Quand la fonction a retourné, la String sur le tas existe toujours, mais tu n'as plus aucun pointeur pour y accéder ou la détruire --> Fuite.

    RVO et NRVO : (Named) Return Value Optimization.
    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.

  19. #19
    Membre du Club
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 57
    Points : 53
    Points
    53
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Pour auto_ptr<>, je ne sais pas trop, je dois avouer que je ne l'utilise jamais...

    La fuite mémoire du premier post: Tu alloues une String sur le tas, mais tu retournes par valeur une copie de cette String. Quand la fonction a retourné, la String sur le tas existe toujours, mais tu n'as plus aucun pointeur pour y accéder ou la détruire --> Fuite.
    Je crois que je suis perdu entre les tas et les piles .. carte .. non GPS bienvenu !

    Ok, par contre je vois le pb quand dans cet exemple, le return *temp renvoie non l'objet alloue precedemment mais une autre copie.


    RVO et NRVO : (Named) Return Value Optimization.
    C'est malin, l'acronyme ! Mais encore ?

  20. #20
    Membre éclairé

    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    717
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2006
    Messages : 717
    Points : 858
    Points
    858
    Par défaut
    Pour la fuite mémoire c'est simple : à chaque new doit correspondre un delete. Tu fait appel à un new mais à aucun delete => fuite mémoire.

    En ce qui concerne std::auto_ptr<> c'est un pointeur intelligent conçu pour maintenir un seul propriétaire à un objet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::auto_ptr<Objet> p1(new Objet); // p1 est LE propriétaire de l'objet
    std::auto_ptr<Objet> p2 = p1; // transfère de propriété : maintenant c'est p2 qui est LE propriètaire
    assert(p2.get() == NULL); // et p1 a été mis à NULL
    Les boost::shared_ptr<> servent au contraire à partage un objet entre plusieurs propriétaires :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    boost::shared_ptr<Objet> p1(new Objet); // p1 est UN propriétaire de l'objet
    boost::shared_ptr<Objet> p2 = p1; // p2 est UN autre propriètaire de l'objet
    assert(p1 == p2); // p1 et p2 pointent sur le même objet
    L'objet est détruit automatiquement lorsqu'il n'a plus de propriétaire.

    Pour NRVO, google doit être ton meilleur ami.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 2
    Dernier message: 31/10/2005, 17h25
  2. Fonction retournant un pointeur
    Par Le Furet dans le forum C
    Réponses: 8
    Dernier message: 25/09/2005, 18h54
  3. Réponses: 17
    Dernier message: 24/03/2005, 12h24
  4. fonction qui retourne un pointeur
    Par sorari dans le forum C++
    Réponses: 6
    Dernier message: 16/03/2005, 21h23
  5. Declaration de fonction retournant un pointeur sur fonction
    Par pseudokifaitladifférence dans le forum C
    Réponses: 5
    Dernier message: 11/08/2003, 19h37

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