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 :

Arrêt inattendu du programme


Sujet :

C++

Vue hybride

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 20
    Par défaut Arrêt inattendu du programme
    Bonjour,

    Le programme dont les sources sont ci-dessous s'arrête de façon inattendue, mais je n'arrive pas à comprendre pourquoi.

    Code main.cpp : 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
    #include <iostream>
    #include <vector>
     
    #include "B.hpp"
    #include "A.hpp"
     
     
    using namespace std;
     
     
    int main(){
     
        vector<A> listeDeA;
     
        int nbA=10;
     
        // We create the agents
        for(int i=0;i<nbA;i++){
            cout << "-------- Creation of the A number " << i << "/" << nbA-1 << " --------" << endl;
            A a;
            cout << "1" << endl;
            listeDeA.push_back(a);
            cout << "2" << endl << endl;
        }
        cout << "OK" << endl;
     
        return EXIT_SUCCESS;
     
    }


    Code A.hpp : 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
    #ifndef _A_HPP_
    #define _A_HPP_
     
     
    class A{
     
        public:
     
            B* b;
     
            //////////
     
            A();
            ~A();
     
    };
     
     
    #endif


    Code A.cpp : 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
    #include <iostream>
     
    #include "B.hpp"
    #include "A.hpp"
     
    using namespace std;
     
     
    A::A(){
        cout << "In the A constructor" << endl;
        b=new B();
    }
     
     
    A::~A(){
        cout << "In the A destructor" << endl;
        if(b!=NULL){
            delete b;
        }
    }




    Code B.hpp : 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
    #ifndef _B_HPP_
    #define _B_HPP_
     
    class B{
     
        private:
     
            int *n1;
            float **n2;
            float **n3;
            float ***n4;
     
        public:
     
            B();
            ~B();
     
    };
     
     
     
     
    #endif




    Code B.cpp : 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
    #include <iostream>
    #include "B.hpp"
     
     
    using namespace std;
     
     
     
    B::B(){
        cout << "In the B constructor (" << this << ")" << endl;
    }
     
     
    B::~B(){
        cout << "In the B destructor (" << this << ")" << endl;
    }


    Lorsque je lance le programme, il s'arrête à la 7e itération du for de main.cpp.

    Le code retourné est -1073741819.

    Merci de votre aide !

  2. #2
    Rédacteur

    Avatar de ram-0000
    Homme Profil pro
    Consultant en sécurité
    Inscrit en
    Mai 2007
    Messages
    11 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Consultant en sécurité
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2007
    Messages : 11 517
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    A a;
    listeDeA.push_back(a);
    Dans la 1ere ligne, tu créés un objet A et dans le constructeur de A, tu alloues un objet B ==> OK

    Dans la 2eme ligne, tu copies l'objet A dans la liste et pour cela, tu utilises le constructeur par copie de A. Tu n'as pas créé de constructeur de copie explicite donc le compilateur utilise le constructeur de copie par défaut qui copie les variables membres.

    Donc tu te retrouves avec 2 objets, la variable locale "a" et la liste qui pointe sur le même objet B. Forcément cela va poser un problème lorsque tu vas détruire la variable "a" puis ensuite la liste, tu vas avoir un double delete du même objet.
    Raymond
    Vous souhaitez participer à la rubrique Réseaux ? Contactez-moi

    Cafuro Cafuro est un outil SNMP dont le but est d'aider les administrateurs système et réseau à configurer leurs équipements SNMP réseau.
    e-verbe Un logiciel de conjugaison des verbes de la langue française.

    Ma page personnelle sur DVP
    .

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    20
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 20
    Par défaut
    Merci de votre réponse, mais pour éviter le double delete j'ai bien écrit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if(b!=NULL){
            delete b;
        }

  4. #4
    Rédacteur

    Avatar de ram-0000
    Homme Profil pro
    Consultant en sécurité
    Inscrit en
    Mai 2007
    Messages
    11 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Consultant en sécurité
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2007
    Messages : 11 517
    Par défaut
    Non, tu as 2 objets, ta variable locale "a" et dans ta liste qui pointent vers la même variable B.

    Donc lorsque tu détruis les 2 variables, tu as bien un double delete.
    Raymond
    Vous souhaitez participer à la rubrique Réseaux ? Contactez-moi

    Cafuro Cafuro est un outil SNMP dont le but est d'aider les administrateurs système et réseau à configurer leurs équipements SNMP réseau.
    e-verbe Un logiciel de conjugaison des verbes de la langue française.

    Ma page personnelle sur DVP
    .

  5. #5
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Par ailleurs, écrire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if(b!=NULL){
            delete b;
        }
    Est inutile : Si b vaut NULL, delete b; est une expression bien valide, au comportement défini, et qui ne fait rien.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    20
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 20
    Par défaut
    Par ailleurs, écrire :
    Code :

    if(b!=NULL){
    delete b;
    }


    Est inutile : Si b vaut NULL, delete b; est une expression bien valide, au comportement défini, et qui ne fait rien.
    Ah, merci de me le signaler ! C'est bien ce que je pensais avant que quelqu'un me dise le contraire !

    J'ai compris pour l'histoire du double delete.

    Dans mon cas, comment éviter cela ?

    Si je fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    listeDeA.push_back(A());
    La même chose se produit (car c'est un temporaire anonyme)...

    Suis-je obligé d'avoir un vector de pointeurs ?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut, et bienvenue sur le forum

    C'est tout le problème du constructeur de copie fourni par le compilateur (et qui est utilisé)

    Je vais considérer la variable a comme étant celle que tu crées toi même et la variable a' comme étant la copie créé en utilisant le constructeur par copie créé par le compilateur:

    a alloue la mémoire pour son membre b...

    On obtiens donc a.b = 0x784520 (par exemple... c'est l'adresse qui a été trouvée lors de l'allocation )

    quand a' est créé, c'est une copie "membre à membre" qui est effectuée...

    Comme un pointeur n'est jamais que "l'adresse à laquelle on va retrouver l'instance d'un objet", lorsque a'.b est créé, l'adresse est tout simplement recopiée

    on a donc a'b = 0x784520

    comme a' est une variable tout à fait indépendante de a, quand a est détruite (parce que l'on sort de la portée dans laquelle elle a été déclarée), le test if(b!=NULL) est bel et bien vérifié (a.b ==...0x784520 et ne vaut donc pas NULL )

    Pour ton malheur, quand il s'agit de détruire l'instance a', le test if (b!=NULL) est également vérifié (vu que a'.b ==...0x784520 ... aussi)...

    Seulement, voilà... l'adresse qui avait été allouée dynamiquement en 0x784520, elle a déjà été libérée dynamiquement lors de la destruction de a ...

    Et tu as donc bel et bien une double libération de la mémoire .

    Pour éviter ce fait, il existe plusieurs solutions (classées par ordre de préférence)
    1. éviter l'utilisation de pointeur lorsque ce n'est pas nécessaire...: finalement, pourquoi ne pas, tout simplement, déclarer le membre b comme... un objet tout à fait normal ainsi, chaque instance de A aura son propre membre, et il n'y aura plus de risque de double libération
    2. Si ce n'est pas possible, veiller à ce que la durée de vie du membre b ne dépende pas de l'instance de A: donner la responsabilité de la destruction du membre à une classe tierce (principe des patterns observer et mediator, par exemple),
    3. (ex-aequo avec (2) )Interdire l'utilisation du constructeur par copie en le déclarant (sans le définir) dans l'accessibilité private:... il faut alors aussi prévoir de faire pareil avec l'opérateur d'affectation
    4. Définir le constructeur par copie pour ta classe A, en veillant à ce que le membre b de la copie ne pointe pas sur le membre b de l'original (nécessite l'allocation dynamique d'un nouveau membre pour la copie )... Mais prévoir également de définir l'opérateur d'affectation

    Dans l'ordre d'esprit du (1), il faut signaler que le problème est également présent avec les pointeur de pointeur (de pointeur (de pointeur) )
    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

  8. #8
    Membre averti
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    20
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 20
    Par défaut
    Pour éviter ce fait, il existe plusieurs solutions (classées par ordre de préférence)

    1. éviter l'utilisation de pointeur lorsque ce n'est pas nécessaire...: finalement, pourquoi ne pas, tout simplement, déclarer le membre b comme... un objet tout à fait normal ainsi, chaque instance de A aura son propre membre, et il n'y aura plus de risque de double libération
    2. Si ce n'est pas possible, veiller à ce que la durée de vie du membre b ne dépende pas de l'instance de A: donner la responsabilité de la destruction du membre à une classe tierce (principe des patterns observer et mediator, par exemple),
    3. (ex-aequo avec (2) )Interdire l'utilisation du constructeur par copie en le déclarant (sans le définir) dans l'accessibilité private:... il faut alors aussi prévoir de faire pareil avec l'opérateur d'affectation
    4. Définir le constructeur par copie pour ta classe A, en veillant à ce que le membre b de la copie ne pointe pas sur le membre b de l'original (nécessite l'allocation dynamique d'un nouveau membre pour la copie )... Mais prévoir également de définir l'opérateur d'affectation

    Dans l'ordre d'esprit du (1), il faut signaler que le problème est également présent avec les pointeur de pointeur (de pointeur (de pointeur) )
    Merci, c'est exactement ce que je cherchais

    finalement, pourquoi ne pas, tout simplement, déclarer le membre b comme... un objet tout à fait normal
    Dans mon cas, c'est pour avoir un truc plus propre, n'allouer la mémoire que lorsqu'on en a besoin (dans le code duquel est parti mon problème bien sûr).

    J'ajouterais à ta liste de solutions, bien que ce soit moins élégant, l'utilisation de listeDeA comme un vector<A*> :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    for(int i=0;i<nbA;i++){
       listeDeA.push_back(new A());
    }
    Merci à tous

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

Discussions similaires

  1. Réponses: 4
    Dernier message: 09/05/2012, 09h47
  2. Réponses: 6
    Dernier message: 10/06/2009, 14h27
  3. Réponses: 25
    Dernier message: 18/12/2007, 22h06
  4. [vb5]Arrêt impromptu de programme
    Par AdHoc dans le forum VB 6 et antérieur
    Réponses: 7
    Dernier message: 20/11/2006, 17h28
  5. Point d'arrêt dans le programme
    Par jmde dans le forum Access
    Réponses: 9
    Dernier message: 20/10/2005, 21h06

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