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 :

conversion raw pointer unique_ptr


Sujet :

C++

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France, Val de Marne (Île de France)

    Informations forums :
    Inscription : Mai 2010
    Messages : 14
    Points : 19
    Points
    19
    Par défaut conversion raw pointer unique_ptr
    Bonjour,

    j'ai un objet map qui me donne diverses informations. Une de ces informations est une largeur que je récupère grâce au code

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    map->GetTileset(i)->GetTileWidth()
    Je voulais raccourcir ce code avec

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      auto tileset = make_unique<const Tmx::Tileset>;
      tileset = map->GetTileset(i);
    Mais j'obtiens l'erreur :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    error: cannot convert ‘const Tmx::Tileset*’ to ‘std::unique_ptr<const Tmx::Tileset, std::default_delete<Tmx::Tileset> > (*)()’ in assignment
       tileset = map->GetTileset(0);
    Le code suivant marche sans problème

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      const Tmx::Tileset* tileset = new const Tmx::Tileset;
       tileset = map->GetTileset(i);
    Du coup, je me demande si on peut convertir un pointer en unique_ptr, ou si il vaut mieux passer par new dans le cas présent.

  2. #2
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Pourquoi ne pas simplement copier le pointeur ? auto tileset = map->GetTileset(i);.

    Mais qui est le propriétaire, la map ou celui qui fait appel à GetTileset ?

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France, Val de Marne (Île de France)

    Informations forums :
    Inscription : Mai 2010
    Messages : 14
    Points : 19
    Points
    19
    Par défaut
    Alors, c'est peut être là que je ne comprend pas ce que je fais. Dans les exemples (l'objet map est créé à partir d'une bibliotèque nommée tmxparser), ils utilisent directement

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     const Tmx::Tileset *tileset = map->GetTileset(i);
    et pas d'opérateur new. Mais, en faisant ça, on copie le pointeur et cette copie ne se désalloue pas (fuite de mémoire ?).

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 965
    Points
    32 965
    Billets dans le blog
    4
    Par défaut
    Salut,

    pourquoi y'aurait-il une fuite mémoire ?
    Si l'objet map fournit des pointeurs, y'a fort à parier que c'est lui qui gère les Tileset. Donc si tu détruits la map, les Tileset qu'il contient seront supprimés...
    Pour ça, faut lire la doc.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,
    Citation Envoyé par Kafkana Voir le message
    Bonjour,

    j'ai un objet map qui me donne diverses informations. Une de ces informations est une largeur que je récupère grâce au code

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    map->GetTileset(i)->GetTileWidth()
    Je voulais raccourcir ce code avec

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      auto tileset = make_unique<const Tmx::Tileset>;
      tileset = map->GetTileset(i);
    Mais j'obtiens l'erreur :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    error: cannot convert ‘const Tmx::Tileset*’ to ‘std::unique_ptr<const Tmx::Tileset, std::default_delete<Tmx::Tileset> > (*)()’ in assignment
       tileset = map->GetTileset(0);
    Mais réfléchis un tout petit peu!!!
    Si tu écris le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
      auto tileset = make_unique<const Tmx::Tileset>;
    quel sera le type que le compilateur va déduire pour tileset, selon toi ?

    Je te le donne dans le mille : ce sera un std::unique_ptr<const Tmx::Tileset> !

    Or, si map->getTileset renvoie un pointeur, ce sera un pointeur de type /*const */ Tmx::Tileset * Or, l'affectation et la copie sont interdites pour les std::unique_ptr, même si map->getTileset renvoyait un std::unique_ptr<const Tmx::Tileset>, tu ne pourrait pas écrire le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    tileset = map->GetTileset(i);
    , parce que cela ferait appel, au mieux au constructeur de copie de std::unique_ptr (qui est désactivé, pour garantir le coté "unique"), au pire à l'opérateur d'affectation, qui est, lui aussi, désactivé pour garantir le coté unique.

    Si tu veux pouvoir récupérer dans tileset le pointeur renvoyé par map->getTileset, il faut le faire correctement : en utilisant la seule fonction qui te permettra d'affecter une nouvelle adresse à ton std::unique_ptr : la fonction reset(), sous une forme qui serait sans doute proche de tileset.reset(map->getTileset(i));.

    Mais, en agissant de la sorte, tu risque de "souffrir" d'un effet secondaire sans doute non désiré : l'objet sous-jacent pointé par ton pointeur (donc, celui qui se trouve à l'adresse mémoire connue par ton pointeur intelligent) sera systématiquement détruit à chaque appel de la fonction reset, car std::unique_ptr veille à ce que cela fonctionne de la sorte (c'est normal : il se considère à juste titre comme... l'unique propriétaire de l'objet sous-jacent et veille systématiquement à le détruire avant de perdre toute référence dessus )
    Le code suivant marche sans problème

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      const Tmx::Tileset* tileset = new const Tmx::Tileset;
       tileset = map->GetTileset(i);
    Il ne saurait pas, il n'a pas de jambes

    Et, surtout, à moins que tu ne fasse "quelque chose" entre ces deux instructions, c'est justement là que tu as une fuite de mémoire, car tu perd toute référence à l'objet qui se trouve à l'adresse mémoire renvoyée par new au moment où tu affecte à tileset l'adresse de l'objet que map->getTileset a renvoyée. Et, si tu n'as pas pris soin de libérer correctement l'objet qui se trouve à l'adresse mémoire connue par tileset AVANT de lui affecter une nouvelle adresse mémoire, il s'agit d'une adresse mémoire que l'on ne pourra plus retrouver
    Du coup, je me demande si on peut convertir un pointer en unique_ptr, ou si il vaut mieux passer par new dans le cas présent.
    Ce n'est malheureusement pas avec ces deux malheureuses lignes que l'on pourra te répondre, ne serait-ce que parce qu'elles sont toutes les deux fausses, et que l'on n'a aucun moyen de savoir de quoi il retourne.

    Par défaut, je recommanderais très fortement l'utilisation des pointeurs intelligents, ne serait-ce que parce que tu sembles encore loin d'avoir compris toutes les subtilités liées à l'utilisation de l'allocation dynamique de la mémoire.

    Et même si tu avais compris, je recommanderais encore les pointeurs intelligents, ne serait-ce que parce que c'est un moyen simple et efficace pour obtenir un code robuste.

    Mais, avant d'en arriver à ces recommandations, il y a beaucoup de questions auxquelles tu devrait apporter une réponse, dont :
    • à quoi sert le new / make_unique de la première ligne
    • y a-t-il "quelque chose" entre les deux lignes que tu nous montre
    • qui est responsable de la durée de vie des pointeurs renvoyés par map
    • Quel est le type de map une classe que tu as créée par toi-même, sur laquelle tu as un minimum de contrôle, ou une classe fournie par une bibliothèque tierce, à laquelle tu ne peux pas toucher
    • j'en passe, et sans doute de meilleures
    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

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France, Val de Marne (Île de France)

    Informations forums :
    Inscription : Mai 2010
    Messages : 14
    Points : 19
    Points
    19
    Par défaut
    Merci pour ta réponse exhaustive.

    En effet ma connaissance des pointeurs est limitée. Le type de map est fournit par une bibliothèque, je n'y ai pas accès. Mais je pense avoir eu la réponse à ma question. Quand je fais

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     const Tmx::Tileset *tileset = map->GetTileset(i);
    la mémoire est déjà alloué dans map, je n'ai pas besoin de la réalloué. Du coup ma question n'a plus de sens. Par contre, il faut être sur de ne plus utiliser le pointeur une fois map détruite.

    Merci à vous

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Kafkana Voir le message
    Merci pour ta réponse exhaustive.

    En effet ma connaissance des pointeurs est limitée. Le type de map est fournit par une bibliothèque, je n'y ai pas accès. Mais je pense avoir eu la réponse à ma question. Quand je fais

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     const Tmx::Tileset *tileset = map->GetTileset(i);
    la mémoire est déjà alloué dans map, je n'ai pas besoin de la réalloué. Du coup ma question n'a plus de sens. Par contre, il faut être sur de ne plus utiliser le pointeur une fois map détruite.

    Merci à vous
    Mais, ca, ce n'est a priori pas un problème, à moins que tu ne décide de placer le pointeur obtenu grâce à getTileset dans une autre collection.

    En effet, map a normalement une durée de vie (beaucoup) plus longue que ne l'est la durée de vie de tileset, vu qu'elle existe déjà au moment où tu déclares ta variable tileset. Cela implique que, quoi qu'il arrive, tileset (la variable, de type pointeur sur Tmx::Tileset, et non l'objet qui se trouve à l'adresse mémoire représentée par ce pointeur ) sera automatiquement détruite avant que map ne le soit (cf "portée des variables")

    Le seul cas un peu limite serait un code 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
     
    std::vector<const Tmx::Tileset *> ptrTab;
    {
        /* map serait créée APRES ptrTab... 
         * Quelque part ici, dans l'ordre logique d'exécution
         */
        for(size_t i = 0; i<map.size(); ++i){
            const Tmx::Tileset *tileset = map->GetTileset(i);
            ptrTab.push_back(tileset); // OUPPPSS... ptrTab contient maintenant un pointeur 
                                       // qui risque d'être invalidé lorsque map est détruite
                                       // (du moins, si map est responsable de la durée de vie
                                       // des éléments pointés par les pointeurs qu'elle contient)
        }
        // pas de problème ici : map existe toujours, ptrTab contient donc des pointeurs invalide
        /* ... */
    }   // CRACK !!! map est détruite ici, les pointeurs qu'elles contenait (et qui sont copiés dans
        // ptrTab) sont potentiellement invalidés à cause de la destructions des objets pointés
        // correspondants
    for(size_t i=0; i<ptrTab.size();++i){
        ptrTab[i]->doSomething(); // BOUM !!! déférencement d'un pointeur invalide...
                                  // ==> erreur de segmentation dans le meilleur des cas
                                  // ==> comportement indéfini aux conséquences catastrophiques dans le pire des cas
    }
    Mais attention : on arrive encore assez facilement à des situations de ce genre
    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

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

Discussions similaires

  1. Réponses: 2
    Dernier message: 10/09/2009, 08h30
  2. Réponses: 0
    Dernier message: 10/04/2009, 11h21
  3. Conversion d'un type raw en varchar
    Par nabelou1 dans le forum SQL
    Réponses: 4
    Dernier message: 12/06/2008, 17h58
  4. Conversion d'un Long raw en Blob
    Par Chantalg dans le forum Oracle
    Réponses: 1
    Dernier message: 08/02/2007, 19h17
  5. Delphi + Conversion Oracle vers Access + LONG RAW
    Par jleg dans le forum Bases de données
    Réponses: 3
    Dernier message: 09/03/2006, 09h35

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