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

SL & STL C++ Discussion :

modification inattendue lors de l'appel de vector::erase(vector::begin())


Sujet :

SL & STL C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Invité(e)
    Invité(e)
    Par défaut modification inattendue lors de l'appel de vector::erase(vector::begin())
    Bonjour,

    Nous ne comprenons pas pourquoi le code suivant ne fonctionne pas :

    Nous avons deux objet dans un vector. On supprime le premier élément par erase(begin), mais cela modifie aussi le second élément...

    Le problème se produit avec codegear, visual studio 2008 et gcc.

    Une idée ?

    Merci d'avance

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #include <vector>
    #include <cstdio>
     
    class A {
        int *m_pi;
     
      public:
         A() {
            m_pi = new int[10];
            printf("constructeur %p (%p)\n", this, m_pi);
        } 
     
        A(const A & ref) {
            m_pi = new int[10];
            printf("constructeur recopie %p de %p (%p)\n", this, &ref, m_pi);
        }
     
        ~A() {
            printf("destructeur de %p (%p)\n", this, m_pi);
            delete[]m_pi;
            m_pi = NULL;
        }
        void tell() {
            printf("je suis %p et je pointe sur %p\n", this, m_pi);
        }
    };
     
     
    int main()
    {
        std::vector < A > vect;
     
        vect.push_back(A());
        vect.push_back(A());
     
        for (int i = 0; i < vect.size(); ++i) {
            vect[i].tell();
        }
        vect.erase(vect.begin());
     
        for (int i = 0; i < vect.size(); ++i) {
            vect[i].tell();
        }
     
        vect.erase(vect.begin());
     
        return 0;
    }
    Voilà la trace :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    constructeur 0x22ccfc (0xdf0230)
    constructeur recopie 0xe00298 de 0x22ccfc (0xe002a8)
    destructeur de 0x22ccfc (0xdf0230)
    constructeur 0x22cd00 (0xdf0230)
    constructeur recopie 0xe002d8 de 0xe00298 (0xe002e8)
    constructeur recopie 0xe002dc de 0x22cd00 (0xe00318)
    destructeur de 0xe00298 (0xe002a8)
    destructeur de 0x22cd00 (0xdf0230)
    je suis 0xe002d8 et je pointe sur 0xe002e8
    je suis 0xe002dc et je pointe sur 0xe00318
    destructeur de 0xe002dc (0xe00318)
    je suis 0xe002d8 et je pointe sur 0xe00318
    destructeur de 0xe002d8 (0xe00318)

  2. #2
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Bonjour,

    C'est normal. Lorsqu'on fait un erase ailleurs qu'en fin de vecteur alors tous les éléments qui sont après l'emplacement où l'on à fait l'erase sont copiés en cran en arrière, pour combler le trou.

    Dans ton exemple le deuxième élément est copié à l'emplacement du premier élément qui vient d'être effacé.

    C'est pour ça qu'on préconise d'utiliser d'autres conteneurs que std::vector si il y a nécessité de faire beaucoup d'insertion ou de suppression au milieu. Par exemple sur un vecteur de 200000 éléments v.erase(v.begin()) oblige à décaler les 199999 éléments restant d'un cran en arrière, donc lance 199999 copies.

  3. #3
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Accessoirement, tu as de la chance dans ton malheur : le manque d'un operator=() pourrait conduire à bien pire comme problème.

    La première valeur que tu affiche est l'adresse de ton instance : celle-ci varie si l'objet est déplacé en mémoire (ce qui se produit lorsque tu erase le premier élément de ton vecteur, comme le dit Arzar). C'est un comportement qui est non seulement normal, mais souhaité.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  4. #4
    Invité(e)
    Invité(e)
    Par défaut
    Oui, effectivement, je n'aurais utiliser le pointeur this pour illustrer le problème.

    Regardez de plus près la valeur de m_pi :

    Si on regarde ce bout de code, on voit qu'il produit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        for (int i = 0; i < vect.size(); ++i) {
            vect[i].tell();
        }
        vect.erase(vect.begin());
     
        for (int i = 0; i < vect.size(); ++i) {
            vect[i].tell();
        }
     
        vect.erase(vect.begin());
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    je suis (0) et je pointe sur 0xe002e8
    je suis (1) et je pointe sur 0xe00318
    destructeur de (0) (0xe00318)
    je suis (0) et je pointe sur 0xe00318
    destructeur de (0) (0xe00318)
    Si on tente d'exécuter le code, celui crashe car l'adresse 0xe00318 est libérée deux fois.

    (Effectivement, c'est tout à fait normal qu'un objet change d'adresse dans un vecteur).

  5. #5
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par mabu Voir le message
    Oui, effectivement, je n'aurais utiliser le pointeur this pour illustrer le problème.

    Regardez de plus près la valeur de m_pi :

    Si on regarde ce bout de code, on voit qu'il produit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        for (int i = 0; i < vect.size(); ++i) {
            vect[i].tell();
        }
        vect.erase(vect.begin());
     
        for (int i = 0; i < vect.size(); ++i) {
            vect[i].tell();
        }
     
        vect.erase(vect.begin());
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    je suis (0) et je pointe sur 0xe002e8
    je suis (1) et je pointe sur 0xe00318
    destructeur de (0) (0xe00318)
    je suis (0) et je pointe sur 0xe00318
    destructeur de (0) (0xe00318)
    Si on tente d'exécuter le code, celui crashe car l'adresse 0xe00318 est libérée deux fois.
    Ca, c'est le coté "il me manque un operator=()" que j'ai signalé. erase() effectue une copie qui utilise cet opérateur. Lorsque le déplacement de données se produit,

    * v[1] est copié dans v[0] (le pointeur est copié tel quel; v[1] et v[0] pointent donc sur le même tableau).
    * le destructeur de v[1] est appelé, le pointeur est deleté[] une première fois.

    Puis tu détruit v[0]. C'est là qu'intervient le second delete[].

    Ne pas oublier la règle des 3 : si tu implémente un destructeur, ou un opérateur de copie, ou le constructeur par copie, alors il faut implémenter ces trois méthodes - dans le cas contraire, le compilateur te fournit des méthodes inline triviales pour les remplacer, et ce n'est pas bon pour ce que tu as
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  6. #6
    Invité(e)
    Invité(e)
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Ne pas oublier la règle des 3 : si tu implémente un destructeur, ou un opérateur de copie, ou le constructeur par copie, alors il faut implémenter ces trois méthodes - dans le cas contraire, le compilateur te fournit des méthodes inline triviales pour les remplacer, et ce n'est pas bon pour ce que tu as
    Impec, Merci

Discussions similaires

  1. Réponses: 1
    Dernier message: 20/09/2012, 15h04
  2. Réponses: 5
    Dernier message: 05/09/2007, 16h23
  3. [Language]problème lors de l'appel d'une méthode
    Par Samanta dans le forum Langage
    Réponses: 6
    Dernier message: 18/05/2005, 13h03
  4. Flash lors de l'appel d'un état Crystal report 7
    Par IADJOFOGUE dans le forum SAP Crystal Reports
    Réponses: 1
    Dernier message: 12/05/2005, 11h10
  5. Erreur lors de l'appel d'un page ASP
    Par poirier dans le forum ASP
    Réponses: 2
    Dernier message: 27/08/2004, 15h17

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