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

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 57
    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 chevronné
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    399
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 399
    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
    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.

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

    Informations forums :
    Inscription : Mars 2008
    Messages : 152
    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 averti
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    57
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 57
    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
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    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.

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

    Informations forums :
    Inscription : Mars 2008
    Messages : 152
    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

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

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    @@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.

  9. #9
    Membre chevronné
    Profil pro
    Inscrit en
    Novembre 2003
    Messages
    394
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2003
    Messages : 394
    Par défaut
    Tu m'as l'air un peu perdu. Je me permet de résumé un poil les réponses.
    Citation Envoyé par mailaka Voir le message
    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 ?
    En bref: Ta fonction crée un tableau sur le tas. La responsabilité de ce tableau est transférée au client (en gros, c'est de la responsabilité de celui qui appelle cette fonction de veiller à ce que la mémoire allouée dans ta fonction soit bien desallouée).
    Dans beaucoup d'articles ou de livres on qualifierais ta fonction de "source" (notamment les articles expliquant les auto_ptr).

    Il y a 2 problèmes distincts avec ce code:
    1) Le client de ta fonction ne sait pas a priori quel est le bon opérateur delete à appliquer afin de désallouer le tableau.
    2) Il y a une fuite mémoire potentielle si l'appelant décide de ne pas gérer le pointeur envoyé.

    Illustration du point 1:
    --
    Je décide d'utiliser ta fonction. En pratique tu me fournirais un fichier d'entête avec la déclaration de cette fonction. Un truc du style
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    #ifndef PREMS_HXX
    #define PREMS_HXX
      int * AllocateAndPopulatePrimes(const int n);
    #endif
    Je l'utilise donc:
    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
     
    // Mon Fichier à moi
    #include <prems.hxx>
    int main()
    {
      // Init
      int * lesPremiers = AllocateAndPopulatePrimes(10);
     
      // Je m'amuse avec les premier
      ...
     
      // Nettoyage: ARGH ! quelle fonction appeler ?
      delete lesPremiers;
      // Ou delete[] lesPremiers
    }
    Illustration du point 2:
    --
    Je suis un boulet et je ne suis interessé que par un possible effet de bord de ta fonction mais pas son résultat (lire: programmeur gros crade). Je peux écrire un truc du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #include <prems.hxx>
    int main()
    {
      AllocateAndPopulatePrimes(10);
      // YEP ! C'est de la fuite mémoire
    }
    Solutions Possibles:
    --
    La première des solutions que je ne vois pas mentionnée ici c'est documenter son code. Notamment, la documentation doit stipuler clairement qui assume la responsabilité des objets/tableaux alloués dynamiquement. Ce type de code n'est peut-être pas esthétique, mais il s'en trimbale des centaines d'exemples parmi l'héritage de code que chaque developpeur est amené à manipuler. Donc si tu ne souhaites pas mettre en place des méchanismes spécifiques pour éviter ces problèmes d'utilisation, le minimum vital c'est de documenter.

    Ensuite il existe bien sûr des solutions plus spécifiques qui on été mentionnée dans ce post.
    • Utilisation d'un vecteur (resoud les points 1&2)

    • Utilisation d'auto pointeurs ou pointeurs intelligents: élimine le problème de responsabilité et plus particulièrement le point 2. Cependant cela ne s'applique pas aisément aux tableaux


    Voilou, en espérant que cela aide. Sinon brûlez mon post

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

    Informations forums :
    Inscription : Octobre 2005
    Messages : 57
    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 ?

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

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