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

Langage C++ Discussion :

vector et déstructeur


Sujet :

Langage C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    64
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 64
    Points : 40
    Points
    40
    Par défaut vector et déstructeur
    Bonjour, j'ai des segfaults lorsque j'exécute mon code.

    J'ai une classe comportant un pointeur et un destructeur (version simplifiée de mon 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
    15
    16
     
    class A {
     
    private :
       img *b;
     
    };
     
    A::A() {
       b = new img();
    }
     
    A::~A() {
       if(b)
          delete b;
    }
    Ensuite je construits un vecteur d'objets A et ça plante à cause de l'appel interne de vector au destructeur de A :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    vector <A> v;
     
    for (unsigned i; i < 5; ++i)
       v.push_back(A());
    si je comprends mon débugger, vector réalise des copies d'objets et appelle donc plusieurs fois les destructeurs, puis ça plante avec un *b qui pointe su n'importe quoi dans l'un des destructeurs.

    Si je vire le destructeur ça marche, mais j'aimerais écrire mon code proprement, que faire ? XD

    Merci

  2. #2
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 130
    Points : 33 063
    Points
    33 063
    Billets dans le blog
    4
    Par défaut
    Bonjour,

    effectivement, quand tu mets un objet dans un vector, il va en faire une copie
    Dans ton cas, tu crées un new img à la construction de A, il copiera l'adresse de b dans la copie (opérateur par défaut)
    Quand tu passes au suivant, le précédent A (temporaire qui a servi à la copie) est supprimé, son B avec.
    Quand tu vides les copies, chacun des B est une erreur (plus que) potentielle.

    un A temporaire est créé puis utilisé pour l'affectation, avant d'être détruit

    Ce qui peut être fait amha
    > utiliser un vecteur de pointeurs
    > spécifier les opérations d'affectation et constructeur par copie
    > utiliser un autre type de pointeur (smart/shared, d'autres seront plus à même d'indiquer lequel )
    > utiliser une factory et un manager pour les B
    ... surement d'autres idées plus ou moins loufoques selon ce que tu souhaites aborder

    A toi de choisir tes armes !

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    64
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 64
    Points : 40
    Points
    40
    Par défaut merci
    Merci pour ces solutions, pour l'instant j'ai créé deux méthodes Lock et Unlock pour libérer ou non la mémoire.

    "utiliser une factory et un manager pour les B"

    Tu peux m'en dire plus, je ne connais pas, j'ai cherché un peu sur google, mais sans trop de résultat.

    Merci

  4. #4
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 130
    Points : 33 063
    Points
    33 063
    Billets dans le blog
    4
    Par défaut
    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
    struct B {
     int cpt;
     std::string name;
     B(std::string _name) { cpt = 1; name = _name; }
    };
    struct BFactory 
    {
     B* getB(std::string name) {
      std::map<std::string, B*>::iterator it = mapB.find(name);
      if (it != mapB.end())
      {
        // B existe déjà, on incrémente le compteur et on le retourne
        it->second->cpt++;
        return it->second;
      }
      // not found, create it
      B* r = new B(name);
      mapB[name] = r;
      return r;
     }
     void releaseB(B* b) {
      b->cpt--;
      if (b->cpt == 0)
      {
        // le compteur vaut 0, il s'agissait du dernier B référencé dans notre programme
        std::map<std::string, B*>::iterator it = mapB.find(b->name);
        mapB.erase(it);
      }
     }
     std::map<std::string, B*> mapB;
    };
    Désolé c'est pas le code le plus beau du monde
    Mais l'idée est là

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Salut,

    Dés que tu commence à utiliser les pointeurs (ie que tu alloues de la mémoire pour un membre de ta classe), il faut impérativement que tu te souvienne qu'il y a quatre fonctions particulières qui doivent être prises en compte (définies personnellement ou explicitement désactivées, selon le cas).

    Il s'agit:
    • du constructeur (par défaut)
    • du constructeur par copie à définir s'il y a un sens à permettre la copie de ton objet (pour faire en sorte que chaque instance de ta classe un pointeur qui sur une adresse mémoire "bien à lui"), ou à désactiver (en le déclarant privé sans le définir ou, en le déclarant = delete en c++1 )
    • l'opérateur d'affectation à définir s'il y a un sens à permettre l'affectation de sorte à éviter les fuites mémoires
    • le destructeur, à définir en faisant en sorte qu'il libère correctement la mémoire allouée au pointeur
    Ces quatre fonctions forment ce que l'on appelle les "big four" et interviennent (d'une manière ou de l'autre) dans les "formes canoniques de coplien" (du nom de celui qui a déterminé ces modèles )

    Ceci dit, y a-t-il une raison fondamentale et incontournable à utiliser un pointeur sur b, au lieu d'un objet de type b "classique"

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    64
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 64
    Points : 40
    Points
    40
    Par défaut
    Merci Bousks, j'ai étudié la chose qui pourra me servir plus tard. Mais j'ai pas opté pour cette solution car j'ai beaucoup de classes dans le même cas.

    Merci koala, pour les 4 règles, sinon impossible de ne pas passer par un pointeur.

    Donc en réfléchissant un peu plus j'ai écrit ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    vector<A*> v;
     
    void fonction(void) {
             A **a;
             a = (A**)malloc(sizeof(A*));
     
             *a = new A();
     
             v.push_back(*a);
    }
    Comme ça j'ai un vecteur de pointeurs. J'utilise un pointeur sur pointeur puisque si la fonction se termine elle efface le premier pointage.

    Qu'en pensez-vous ? En tout cas ça marche.

    Ha zut, le malloc c'est pas beau en c++, comment le remplacer par un new ?

    Tant qu'on y est j'ai une autre question, delete(obj); obj vaudra null après un delete ? Merci

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par killwin Voir le message
    Merci Bousks, j'ai étudié la chose qui pourra me servir plus tard. Mais j'ai pas opté pour cette solution car j'ai beaucoup de classes dans le même cas.

    Merci koala, pour les 4 règles, sinon impossible de ne pas passer par un pointeur.
    A vrai dire, les cas d'utilisation de pointeurs sont extrêmement rares!!!!

    Pourquoi dis tu qu'il t'es impossible de ne pas passer par un pointeur

    Ta classe ne pourrait-elle pas ressembler à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class A {
     public:
        A(/* paramètres pour construire l'image*/);
    private :
       img b;
     
    };
    A::A(/* paramètre pour construire l'image*/):b(/*parametres*/)
    {
    }
    Tu t'éviterais ainsi le recours à l'allocation dynamique et tu n'aurais pas besoin de t'embêter avec le constructeur par copie, l'opéateur égal ou le destructeur: ceux qui seraient fournis d'office par le compilateur seraient suffisants
    Donc en réfléchissant un peu plus j'ai écrit ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    vector<A*> v;
     
    void fonction(void) {
             A **a;
             a = (A**)malloc(sizeof(A*));
     
             *a = new A();
     
             v.push_back(*a);
    }
    Ah, bon dieux!!!!! Que ca pique aux yeux!!!!

    Si, vraiment, tu n'as pas d'autre choix d'utiliser un pointeur sur img, soit tu rend, purement et simplement, ta classe non copiable sous une forme 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
    class A
    {
        public:
            A():b(new img){}
            ~A() {delete b;}
            /* en C++11, on peut faire
            A(A const &) = delete;
            A& operator=(A const &)  = delete;
            */
         private
             /* en attendant C++1, on déclare le constructeur par copie et
              * l'opérateur d'affectation privé et on ne les définit pas
              */
             A(A const &);
             A& operator= (A const &);
             img * b; 
    };
    et il s'agira alors d'utiliser un vecteur de pointeurs sur A sous une forme 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
    int main()
    {
        std::vector<A*> tab;
        tab.push_back(new A() );
        /* ou ou ou  */
        A * toadd=new A;
        tab.push_back(toadd);
       /* ne pas oublier, une fois que l'on en n'a plus besoin, de libérer
        * la mémoire reliée à tous les pointeurs qui sont dans le vecteur
        */
        for(std::vector<A*>::iterator it = tab.begin();it!=tab.end();++it)
            delete *it;
        return 0;
    }
    soit on fait en sorte que chaque instance de A (y compris la copie) utilise un pointeur sur img qui lui est propre.

    L'idéal étant alors d'utiliser l'idiome "copy and swap"
    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
    class A
    {
        public:
            A():b(new img){}
            A (A const & other):img(new img(other.b){}
            A & operator  = (A const & other)
            {
                A temp(other);      // va appeler le constructeur par copie
               std::swap(b,temp.b); // interverti le pointeur sur b entre
                                    // l'instance en cours et la copie
               retrurn *this;
            } // la copie est détruite, delete est donc appelé sur temp.b, on évite
              // les fuites mémoire
            ~A(){delete b;}
        private:
            img* delete;
    };
    et, l'utilisation peut rester aussi simple que
    int main()
    {
    std::vector<A> tab;
    tab.push_back(A());
    /* ou ou ou */
    A toadd;
    tab.push_back(toadd);
    retrun 0;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Comme ça j'ai un vecteur de pointeurs. J'utilise un pointeur sur pointeur puisque si la fonction se termine elle efface le premier pointage.
    Oublie la notion de pointeurs pour le passage d'argument à une fonction et pense "référence"...

    De plus, évite autant que possible d'utiliser des variables globales, ca ne présente que des inconvénients (encore plus que les pointeurs de pointeurs dirais-je )
    Si tu veux vraiment qu'il y ait une fonction qui s'occupe de tout (la création du pointeur et l'insertion dans le tableau) écrit plutot quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    vois foo(std::vector<A*> & tab)
    {
         A * temp = new A;
         tab.push_back(temp);
    }
    Qu'en pensez-vous ? En tout cas ça marche.
    [/QUOTE]
    Tant qu'on y est j'ai une autre question, delete(obj); obj vaudra null après un delete ? Merci[/QUOTE]

    D'abord, ce n'est pas delete(obj); mais simplement delete obj; et ensuite, obj ne vaudra null que si tu le met explicitement à nul

    Dans le code de A que j'ai donné, b n'est pas mis à NULL après avoir été détruit, mais ce n'est pas grave: comme l'objet a été détruit, on n'essaiera pas d'accéder à ce pointeur par la suite

    Par contre, si tu utilise un vecteur de pointeurs, il faudra être attentif, si, à un moment, tu souhaites détruire l'un des élément du vecteur:

    Soit tu devra veiller à mettre le pointeur en question à NULL, mais il faudra alors vérifier chaque élément pour s'assurer qu'il ne vaut pas NULL avant d'essayer d'accéder à ce qu'il pointe:

    delete tab[i];
    tab[i] = NULL;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    /* pour l'accès */
    if(tab[i]!= NULL)
    {
        /* ce qu'il faut faire */
    }
    soit, et c'est surement le plus facile, il faut veiller à retirer les pointeurs devenus invalides
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    delete tab[i];
    tab.erase(i);

  8. #8
    Membre du Club
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    64
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 64
    Points : 40
    Points
    40
    Par défaut merci
    Sinon il s'agit d'un pointeur car ce sont plusieurs objets qui travaillent sur une même grosse structure.

    Mais finalement, faudrait que je reprenne tout mon code et me débrouiller pour faire un passage par référence au lieu d'utiliser les pointeurs. Sinon merci je vais étudier tout ça et je vous tien au courant.

  9. #9
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 130
    Points : 33 063
    Points
    33 063
    Billets dans le blog
    4
    Par défaut
    Tu auras le même problème en utilisant un pointeur ou une référence, puisque ce sont grosso merdo les même choses.

    plusieurs objets qui travaillent sur une même grosse structure
    > factory ?

  10. #10
    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 : 122
    Points
    122
    Par défaut
    Je vais peut-être dire une bétise, mais si je pars du code de départ, ne suffirai-t-il pas de remplacer son pointeur par un shared_ptr?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class A {
    A();
       shared_ptr<img> b; 
    };
     
    A::A() : b(new img())
    {
    }
     
     
    //J'omet ici le constructeur par copie et l'operateur = car l'implementation généré par le compilo doit convenir je pense. Idem pour le destructeur

  11. #11
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Citation Envoyé par bountykiler Voir le message
    Je vais peut-être dire une bétise, mais si je pars du code de départ, ne suffirai-t-il pas de remplacer son pointeur par un shared_ptr?
    Ca reste une solution rapide des plus sensées. Ca pourrait même être un std::unique_ptr.
    Bref, une lecture s'impose au po : Présentation des pointeurs intelligents en C++ par Loïc Joly

Discussions similaires

  1. [Struts] logic:iterate avec un Vector
    Par laurentb dans le forum Struts 1
    Réponses: 18
    Dernier message: 03/03/2004, 14h42
  2. vector et erase()
    Par gytr2 dans le forum SL & STL
    Réponses: 6
    Dernier message: 02/03/2004, 12h45
  3. equivalent Vector du jsp
    Par Djib dans le forum ASP
    Réponses: 4
    Dernier message: 05/12/2003, 08h07
  4. "vector" provoque "syntax error", malgré
    Par seenkay dans le forum Autres éditeurs
    Réponses: 5
    Dernier message: 24/08/2003, 03h21
  5. Réponses: 2
    Dernier message: 11/07/2003, 18h24

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