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 :

[explication]Fuite mémoire new()


Sujet :

C++

  1. #1
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 044
    Points : 2 241
    Points
    2 241
    Par défaut [explication]Fuite mémoire new()
    Bonjour,
    Je viens vous demander quelques précisions sur la gestion des exceptions levés par l'opérateur new().
    si j'écrit ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    try
    {
        CClasse *obj = new CClasse()
    }catch(bad_alloc)
    {}
    Ceci me gère-t-il correctement mon problème si j'ai un erreur d'allocation?
    Appele-t-il seul le destructeur de cette classe?
    Une autre question aussi, si j'écrit ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    try
    {
        CClasse obj = new CClasse()
    }catch(bad_alloc)
    {}
    Quel est la différence exactement? il n'est pas allouer dans la pile, mais la gestion de l'exception est-elle gérée de la même manière?

    je vous remercie.

  2. #2
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Cclasse obj = new Cclasse();

    ça compilera pas ça...


    Pour le premier cas : si l'erreur est levée non la ressource ne sera pas libérée. C'est pour ça qu'on utilise le RAII ...


    Pour plus de détails voir la faq .. ici :
    http://cpp.developpez.com/faq/cpp/in...POINTEURS_raii

  3. #3
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 044
    Points : 2 241
    Points
    2 241
    Par défaut
    Oui effectivement j'ai fait un bete copier coler...

    Mais donc pour le premier cas, je n'ai pas la possiblité d'appeler de destructeur si l'exception est levée? ou de libérer les espaces mémoires réservés lors du new()?
    Car si cela échoue, j'ai un fuite mémoire, non?

  4. #4
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Points : 1 174
    Points
    1 174
    Par défaut
    en tout cas normalement tu n'as pas à te soucier de ce genre d'exceptions dans ton programme. En règle générale tu les laisses se propager et terminer le programme. ( ça sert à quoi de continuer si tu ne peux plus rien allouer? )

    sinon une autre page de la FAQ:
    http://cpp.developpez.com/faq/cpp/in...S_constructeur

    ( et bien sûr généralement on se passera des pointeurs quand on peut )

  5. #5
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 044
    Points : 2 241
    Points
    2 241
    Par défaut
    Très bien merci
    Je ne voudrais pas abuser de votre gentilesse mais j'aurais une dernière interrogation.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Tableau = new CConteneur[5]
    ceci peut créer une fuite mémoire n'est-ce pas?
    et je la gère comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    try{
        Tableau = new CConteneur[5]
    }catch(bad_alloc)
    {
    if (Tableau!= 0)
        delete [] Tableau;
    }
    Merci beaucoup de votre aide.

  6. #6
    Membre averti Avatar de Nogane
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 241
    Points : 323
    Points
    323
    Par défaut
    Bonjour,
    A priori, le delete est inutile.
    - Si l'exception est levée pendant l'allocation, il n'y a pas de raison de desaouler.
    - Si elle est lever pendant la construction, le destructeur ne sera pas appelé. Mais la désalocation le sera. La seul chose que tu as a faire est donc de t'assurer que ton constructeur est exception-safe. C'est a dire qu'il n'y aura pas de fuite mémoire dans le cas d'une exception levée. (En général, le RAII est la solution utilisée). Ceci ce fait dans le constructeur(ou dans le choix des membre de ta class), et non dans le bloque catch.

    Plus d'info ici, dans Think in cpp volume 2:
    http://nicolas.blancpain.free.fr/Doc...tml#Heading253

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

    Informations professionnelles :
    Activité : aucun

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

    A vrai dire, il s'agit de faire la différence au sujet du moment où se passe l'allocation dynamique...

    Ainsi, si tu effectue une allocation dynamique sur une classe dont le constructeur n'en fait aucune comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class MaClass
    {
        public:
           MaClass(int i, int j):m_i(i),m_j(j){}
        private:
            int m_i;
            int m_j;
    };
    Tu n'éprouvera aucun problème si un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MaClass *objnew MaClass( i, j );
    lance une exception bad_alloc...

    Par contre, tu risque d'avoir des problèmes si la classe effectue elle-même une (et surtout plusieurs) allocation(s) dynamique(s)

    Imaginons une classe qui n'est absolument pas "exception safe" qui prendrait la forme 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
    24
    25
    26
     
    class Matrice
    {
        public:
            /* pour que l'on puisse utiliser new Matrice[10], la classe
             * doit être défaut constructible
             */
            Matrice(size_t l = 10, size_t c = 10):mligne(l),mcol(c)
            {
                mat = new int[mligne]; // peut lancer bad_alloc (1)
                for(size_t i = 0;i<mligne; ++i)
                    mat[i]= new int[col]; // chaque passage peut lancer 
                                          // bad_alloc (2)
            }
            /* la destruction se fait pourtant correctement */
            ~Matrice()
            {
                for(i = 0; i<mlinge; ++i)
                    delete [] mat[i];
                delete [] mat;
            }
        private:
            int ** mat;
            int mligne;
            int mcol;
    }
    Si bad_alloc est lancé lors de la première allocation dynamique (1), tu n'aura aucune fuite mémoire...

    Mais, si l'allocation dynamique lance une bad_alloc à l'un des passage correspondant au (2)... tu auras "d'office" une fuite mémoire, qui arrivera même sur un "simple"
    Et ça, tu ne pourra rien y faire...

    Pour résoudre le problème, il s'agirait de modifier le constructeur 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
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    class Matrice
    {
        public:
            /* pour que l'on puisse utiliser new Matrice[10], la classe
             * doit être défaut constructible
             */
            Matrice(size_t l = 10, size_t c = 10):mligne(l),mcol(c)
            {
                mat = new int[mligne]; // peut lancer bad_alloc (1)
                for(size_t i = 0;i<mligne; ++i)
                {
                    try
                    {
                        mat[i]= new int[col]; // chaque passage peut lancer 
                                              // bad_alloc (2)
                    }
                    catch (bad_alloc &e)
                    {
                        /* si l'allocation a échoué, il faut libérer la mémoire
                         * de tout ce qui a déjà été alloué
                         */
                       for(int j = 0; j<i; ++j)
                           delete[] mat[j];
                       delete[] mat;
                       /* et de relancer l'exception, pour s'assurer qu'elle
                        * remonte correctement
                        */
                      throw e;
                    }
                }
            }
           /* suite de la classe */
    };
    Mais, si ce constructeur permet d'éviter la fuite mémoire lorsque tu travaille sur une seule instance, comme par exemple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Matrice mat(20,20);
    /* ou */
    Matrice * ptr = new Matrice(20,20);
    que va-t-il se passer si tu travailles avec plusieurs instances Avec un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Matrice * ptr = new Matrice[10];
    Si bad_alloc est lancée, est-elle lancée par le constructeur de Matrice[0] (pas de fuite mémoire), ou par celui de n'importe quelle autre instance

  8. #8
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 044
    Points : 2 241
    Points
    2 241
    Par défaut
    Merci énormement, c'est tout ce dont je pouvais avoir besoin
    Donc les fuites mémoires ne se traitent que dans une allocation dynamique.
    Si je fait un allocation successive il y a des risques de fuite mémoire car il réussira peut-être le premier, pas le deuxième mais marquera cette espace mémoire quand même comme occupé, et pourra très bien réussir le troisième. Ce qui me laissera alors une trou dans ma mémoire à l'emplacement réservé à ma deuxième allocation... si j'ai bien tout compris(Cours de pointeur remonte à loin :p)
    Donc si je fait un :
    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
     
    int i = 0;
    while (i < 10)
    {
          cin >> iNombre;
          try
          {
              CClasse * p = new CClasse(iNombre);
              objTableau.SetNew(*p);
              delete p;
           }catch(bad_alloc &e)
           {
            objTableau.~CTableau();
            break;
            throw e;
           }
            i++;
    }
    si une allocation ne se passe pas bien, je détruit ma classe tableau... et je sort de ma boucle. A partir de la ma fuite mémoire est gérée correctement?

    merci

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 627
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Astraya Voir le message
    Merci énormement, c'est tout ce dont je pouvais avoir besoin
    Donc les fuites mémoires ne se traitent que dans une allocation dynamique.
    Si je fait un allocation successive il y a des risques de fuite mémoire car il réussira peut-être le premier, pas le deuxième mais marquera cette espace mémoire quand même comme occupé, et pourra très bien réussir le troisième. Ce qui me laissera alors une trou dans ma mémoire à l'emplacement réservé à ma deuxième allocation... si j'ai bien tout compris(Cours de pointeur remonte à loin :p)
    Donc si je fait un :
    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
     
    int i = 0;
    while (i < 10)
    {
          cin >> iNombre;
          try
          {
              CClasse * p = new CClasse(iNombre);
              objTableau.SetNew(*p);
              delete p;
           }catch(bad_alloc &e)
           {
            objTableau.~CTableau();
            break;
            throw e;
           }
            i++;
    }
    si une allocation ne se passe pas bien, je détruit ma classe tableau... et je sort de ma boucle. A partir de la ma fuite mémoire est gérée correctement?

    merci
    Attention, on n'invoque jamais soi-même un destructeur...

    Soit il est appelé automatiquement lorsque l'on quitte la portée dans laquelle une variable est déclarée (sans allocation dynamique)

    Soit il est appelé au travers de la libération de mémoire allouée dynamiquement avec delete ou delete[]

    De plus, le break va à l'encontre du throw e car il fait sortir de la boucle juste avant que throw ne soit exécuté ( si tu venais à inverser les deux, ce serait throw qui irait à l'encontre du break )

    En réglant correctement ton compilateur, tu aurais d'ailleurs eu un avertissement sur le fait que la ligne correspondant au throw n'est jamais effectuée

    Il faut donc comprendre le fonctionnement de ces deux méthodes:

    Dans une boucle, break fait "simplement" sortir de la boucle mais permet à ce qui se trouve après la boucle de s'exécuter

    L'exception "remonte" systématiquement jusqu'à la fonction dans laquelle le comportement est placé dans un bloc try où un block catch correspond au type de l'exception lancée...

    Ainsi, si tu avais plusieurs fonctions sous la forme 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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    void foo()
    {
        throw MyException(); // mon exception à moi
    }
    void bar()
    {
        foo(); // appelle foo, mais ne récupère pas l'exception
    }
    void otherBar()
    {
        try // on envisage qu'une exception soit lancée
        {
            bar(); 
        }
        // mais on n'attrappe pas MyExcept :-P 
        catch (OtherException & e)
        {
            /*...*/
        }
    }
    void finalBar()
    {
        try // on envisage qu'une exception soit lancée
        {
            otherBar();
        }
        catch (OtherException & e)
        {
            /*...*/
        }
        catch(MyException &e)
        {
        }
    }
    l'exception lancée dans foo() remontera jusqu'à ce qu'il y ait un catch capable de traiter l'exception de type MyException, c'est à dire, jusqu'à finalBar()

  10. #10
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Pour être tout à fait exacte il existe un cas où on appel explicitement le destructeur, dans le cas d'un placement new...




    (c'est parce que t'a souligné le jamais :p )

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 627
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Goten Voir le message
    Pour être tout à fait exacte il existe un cas où on appel explicitement le destructeur, dans le cas d'un placement new...




    (c'est parce que t'a souligné le jamais :p )
    C'est vrai qu'il ne faut jamais dire jamais

    Je ne pensais plus au placement new au moment d'écrire mon intervention... Mais il faut avouer qu'il est suffisamment rare pour ne pas le faire intervenir dans le problème qui nous occupe ici

  12. #12
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 044
    Points : 2 241
    Points
    2 241
    Par défaut
    Très bien, merci pour tout ses renseignements!
    Donc au lieu d'appeler le destructeur, je peut appeler delete de l'objet dynamique que j'ai voulu créer?
    sachant que mon iTableau est déclaré comme suis
    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
    int i = 0;
    while (i < 10)
    {
          cin >> iNombre;
          try
          {
              CClasse * p = new CClasse(iNombre);
              objTableau.SetNew(*p);
              delete p;
           }catch(bad_alloc &e)
           {
            delete &objTableau.GetElement(i);
            i--; //pour revenir ou j'en étais avant l'erreur
            throw e;
           }
            i++;
    }

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 627
    Points : 30 692
    Points
    30 692
    Par défaut
    Attends un peu, parce que c'est quelques peu confus, tout cela...

    Tu nous parle d'une variable iTableau de type CTableau, dont on ne sait rien,

    Or, dans le code que tu fournis, on dispose d'une variable objTableau, dont, tout ce que l'on sait, c'est qu'il dispose d'une fonction setNew prenant... soit une référence sur CClasse soit une instance de CClasse

    De plus, tu invoque l'allocation dynamique sur un pointeur de type CClasse, pour transmettre ce qui est pointé par ce pointeur à ta variable objTableau, et pour... libérer la mémoire allouée à ce pointeur tout aussi vite...

    Si donc tu essayais de réfléchir un peu à ma signature, et que tu nous expliquais clairement ce que tu cherche à faire, je crois que nous arriverions bien plus vite à une réponse qui puisse te convenir

  14. #14
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 044
    Points : 2 241
    Points
    2 241
    Par défaut
    Oui autant pour moi, ce n'est pas iTableau mais objTableau
    Je suis tout à fait d'accord que ce raisonnement n'est pas très claire , pour moi non plus pour la simple et bonne raison que c'est pas moi qui est fait le code :p. Mon école ma demandé de trouver comment gérer une possible fuite mémoire ici...simpa non?

    CTableau :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    //attributs
               CClasse * Tableau;
    	int iTaille;
    	int iPosition;
    //méthodes
               Constructeur() // Tableau = new CClasse[5];
               SetNew(i); //ajoute un CClasse dans le tableau avec le constructeur de CClasse(int i)
               GetElement(index); //redonne un CClasse à l'index donné
    CClasse :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    //attributs
             int valeur;
    //Constructeur
             CClasse(int i){valeur = i};
    //methodes
             getValeur()
             setValeur()
    Le code que je vous donnai juste avant était dans le main.
    Soit dit en passant dans le constructeur de CTableau il me font un:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    try
    {
    	Tableau = new CClasse [i];
    }
    catch(bad_alloc)
    {
    	this->~CTableau();
    }
    L'appel au constructeur à ce que j'ai compris il ne faut pas sauf dans une cas unique avec le new. Une critique la dessus? :p

  15. #15
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Euh ... oui plus qu'une critique même x). Si t'appelles explicitement le destructeur alors tu t'exposes à ce qu'il soit appelé deux fois. En fonction de ce que fait le dit destructeur ça peut avoir des effets assez... dangereux. (quand on doit faire des désallocations notamment).




    ps : le cas unique c'est le placement new et non pas le new tout court. Mais ne t'encombre pas l'esprit avec ça... j'ai rectifié koala sur ce point mais c'était pas très intéressant dans l'optique du topic là.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 627
    Points : 30 692
    Points
    30 692
    Par défaut
    Houla... oui, il y a des critiques à formuler

    D'abord, dans main, ce n'est surement pas
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Tableaux = new CClasse/*...*/
    qu'il s'agit de faire, car Tableau est une variable membre de la classe CTableau, ce qui implique qu'il faille passer par... une instance de CTableau pour pouvoir manipuler... la variable Tableau

    De plus, on peut estimer, vu que vous avez prévu des mutateurs et des accesseurs (setNew pouvant être considérée comme un mutateur, bien qu'elle serait avantageusement renommée en pushBack ou en add), que le membre Tableau sera déclaré avec une accessibilité protégée, si ce n'est privée...

    Le seul moyen d'y accéder depuis la fonction main étant donc... de passer par setNew ou par GetElement

    Ce devrait donc être au sein de la fonction setNew que tout devrait être géré (vu que c'est, normalement, la seule fonction membre qui semble par son nom habilitée à ajouter un élément de type CClasse à ton instance de CTableau )

    En outre, le seul moyen de pouvoir écrire, à n'importe quel moment (que ce soit
    dans une des méthodes de CTableau ou dans main) un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    CClasse *ptr = new CClasse[x];
    est que la classe CClasse soit défaut constructible (c'est à dire: que le constructeur de cette classe ne nécessite aucun argument pour fonctionner)... Or ce n'est pas le cas

    Enfin, plutôt que de s'amuser à jouer avec les pointeurs et les allocations dynamiques qui sont vraiment loin d'être sécurisantes, il serait surement intéressant de passer par l'un des conteneurs de la STL, tel que std::vector (pour aller avec la fonction membre getElement)

    Au passage, nous pourrions insister sur l'utilité des listes d'initialisation dans les constructeurs, et sur l'intérêt représenté par le fait d'avoir une politique de nommage cohérente (Il serait pas mal d' "homogénéiser" les noms des différents attributs: entre iMachinChose, Tableau et valeur... il y a de quoi s'y perdre)

    Enfin, toutes ces remarques ayant été faites, je vais te donner plusieurs solutions, en présumant que iTaille correspond à la taille du tableau de CClasse et que iPosition correspond à la position du dernier élément inséré

    Si tu te tourne vers une gestion "manuelle" dynamique du membre Tableau de ta classe CTableau (ce que je suis très loin de conseiller), il faut, comme pré-requis:
    1. rendre ta classe CClasse défaut constructible
    2. veiller à ce que iTaille et iPosition soient correctement initialisés dans le constructeur de CTableau (respectivement à 5 et à 0)
    3. Veiller enfin à ce que l'allocation dynamique du membre Tableau soit correctement effectuée en vue de lui faire accepter 5 éléments de types CClasse


    Cela se traduit par la création (mais a-t-elle un sens ) d'un constructeur ne prenant pas d'argument pour CClasse, ressemblant à s'y méprendre à
    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
    class CClasse
    {
        public:
            CClasse():valeur(0) // mais une valeur à 0 est-elle cohérente ???
            {
            }
            CClass(int i):valeur(i)
            {
            }
            /* fonctions membres get et set à rajouter ;)
             * PS : n'oublie pas de rendre la fonction membre get constante ;)
             */
        private:
            int valeur;
    };
    La classe CTableau deviendrait alors 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
    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
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
     
    class CTableau
    {
        public:
            CTableau():iPosition(0),iTaille(5)
            {
                Tableau = new CClasse(iTaille); /* laissons lancer l'exception
                                                  * bad_alloc sans nous en
                                                  * inquiéter ;) 
                                                  * De toutes manières, si elle
                                                  * est lancée, à part quitter 
                                                  * l'application, il n'y aura pas 
                                                  * énormément de choix
                                                  * si l'instance de CTableau est
                                                  * créée dans main() ;)
                                                  */
            }
            /* Il serait intéressant de savoir si CTableau est copiable et
             * assignable ou non... je vais partir du principe qu'elle n'est
             * ni l'un ni l'autre, autrement, il s'agirait d'utiliser l'idiome
             * " copy and swap " pour en permettre la copie et l'assignation ;)
             */
            ~CTableau()
            {
                delete[] Tableau;
            }
            void setNew(int toadd)
            {
                /* la première chose à faire, c'est de s'assurer qu'il reste 
                 * de la place dans le tableau pour recevoir un nouvel élément
                 */
                if(iPosition == (iTaille-1) // si ce n'est pas le cas
                {
                    /* il faut réallouer de l'espace...
                     * un bon moyen d'éviter de le faire trop régulièrement
                     * est de multiplier la taille par 1.5
                     */
                   /* ici, si l'allocation échoue, nous laissons l'exception
                    * bad_alloc etre lancée, sans nous en occuper
                    * (nous laisserons à la fonction appelante le soin
                    * de savoir quoi faire, mais il n'y aura pas de fuite
                    * mémoire pour la cause ;) )
                    */
                   CClass * temp = new CClass[iTaille * 1.5]; 
                   /* Nous copions les éléments de Tableau dans temp */
                   for(int i = 0;i<iTaille;++i)
                       temp[i] = Tableau[i];
                   /* nous libérons la mémoire allouée à Tableau */
                   delete[] Tableau ;
                   /* nous assignons l'adresse de temp à Tableau */
                  Tableau = temp;
                   /* nous terminons en mettant la taille à jour */
                   iTaille *=1.5;
                }
                /* En arrivant ici, nous avons la certitude que l'élément 
                 * se trouvant à iPosition + 1 est accessible...
                 * Nous n'avons qu'à assigner la valeur adéquate
                 * et à mettre la position à jour ;)
                 */
                 ++iPosition;
                Tableau[iPosition].setValeur(toadd);
            }
        private:
            /* interdisons la copie et l'assignation (ne surtout pas définir
             * ces deux fonctions membres)
             */
            CTableau(CTableau const &);
            CTableau& operator=(CTableau const &);
            /* et les membres intéressants */
            int iPosition;
            int iTaille;
            CClasse *Tableau;
    };
    Mais il y a une solution bien plus facile que cela... qui ne nécessite déjà pas de rendre la classe CClasse défaut constructible, (car elle nécessite uniquement que CClasse soit copiable), qui permet que CTableau reste copiable et assignable, et qui ne nécessite pas de jouer avec l'allocation dynamique de la mémoire, ni même de disposer de iPosition et de iTaille...

    Tu avoueras, à la lecture de ces avantages, que cette solution "a tout pour plaire"

    Cette solution est, tout simplement, basée sur l'utilisation de la classe vector, fournie par le standard dans l'espace de noms std, par simple inclusion du fichier d'en-tête <vector>.

    Ta classe CTableau deviendrait "simplement":
    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
    class CTableau
    {
        public:
            CTableau():Tableau() /* cet appel au constructeur par défaut
                                     * de la classe vector est simplement
                                     * conseillé, mais pas indispensable
                                     */
            {
            }
            /* si tu tiens à définir le destructeur */
            ~CTableau()
            {
                /* Rien... le destructeur de la classe vector s'occupe de 
                 * tout...
                 *
                 * Elle est pas belle, la vie ???
                 */
            }
            void setNew(int i)
            {
                /* un code vraiment compliqué :D */
               Tableau.push_back(CClasse(i));
               /* C'est tout :D */
            }
            const CClasse& getElement(size_t index) const
            {
               /* la fonction membre at de la classe
                * std::vector lance une exception si l'index indiqué
                * est trop grand, profitons en :D 
                */
              return Tableau.at(index);
            }
        private:
            /* pas besoin de s'amuser à rendre la classe non copiable
             * et non assignable... contentons nous des membres ;)
             */
           std::vector<CClasse> Tableau;
           /* Et c'est tout: la classe vector gère elle-même le nombre
            * d'élément qu'elle peut contenir et le nombre d'éléments
            * qu'elle contient :D
            */
    }
    Au final, tu remarqueras que les commentaires prennent plus de place que le code réellement utile

    [EDIT]Et, dans main, tu te trouvera dans la situation suivante:
    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
    int main()
    {
        /* si l'allocation échoue au départ, nous laissons quitter
         * l'application (problème "d'initialisation" ;) )
         */
        CTableau tab;
        bool encore = true; // pourquoi placer une limite au nombre d'éléments ?
        size_t count = 1 ; //par contre, on peut compter les éléments placés
        do       // il faut au moins insérer un élément
        {
            std::cout<<"introduisez la valeur pour le "
                     <<count<<"eme élément :";
            int i;
            std::cin>>i;
            try
            {
                //tentons d'ajouter l'élément
                tab.setNew(i);
                //arrivés ici, elle a réussi
                ++ count;
                std::cout<<"une autre introduction ? (O / N ):";
                char a;
                cin>>a;
                encore = (a=='o' || a=='O');
            }
            catch( std::bad_alloc &e)
            {
                /* par contre, si elle a échoué, nous prévenons l'utilisateur */
                std::cout<<"impossible d'allouer la mémoire pour ajouter l'élément"
                             <<count<<std::endl;
                /* forcons la boucle à être quittée */
                encore = false;
            }
        }while (encore);
        /* travail à effectuer une fois le tableau rempli */
        return 0;
    }
    (code potentiellement à améliorer et non testé )
    [/EDIT]

  17. #17
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 044
    Points : 2 241
    Points
    2 241
    Par défaut
    Euh ... oui plus qu'une critique même x). Si t'appelles explicitement le destructeur alors tu t'exposes à ce qu'il soit appelé deux fois. En fonction de ce que fait le dit destructeur ça peut avoir des effets assez... dangereux.
    Comme quoi l'école et la programmation... :p

    Bas écoutez ça me vas à merveille Merci énormement de ces lumières, j'était un peut pommé dans la ce genre de truc... Vous n'aurez pas un lien ( sans vouloir abuser et trop vous en demander :p) qui explique clairement les fuites mémoires? les endroits ou elle peuvent apparaitre, les choses à éviter pour les limiter, les solutions apportées? genre une page web, un ppt ou autres, sa m'interresse pas mal d'en savoir un peu plus la dessus

    Merci à vous en tout cas!

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 627
    Points : 30 692
    Points
    30 692
    Par défaut
    La règle est simple pour les éviter: évite les pointeurs et les allocations dynamiques

    Le standard fournit tout un arsenal de conteneurs qui te facilitent énormément la tache et qui sont particulièrement sécurisés à ce niveau... Autant les utiliser à chaque fois que tu veux manipuler collections d'objets.

    Tu trouvera dans la faq un diagramme t'aidant à choisir le conteneur le plus adapté à tes besoins

    Si, malgré tout, tu te trouve face à la nécessité absolue de manipuler des pointeurs (pour gérer, par exemple, le polymorphisme), pense au principe RAII (Ressource Acquisition Is Initialisation) et au principe inverse (Ressource Relase On Destruction), évite, si tu le peux, les new multiples et en cascade et veille à ce qu'il corresponde un delete ou un delete[] à chaque new ou new[].

  19. #19
    Membre chevronné Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 044
    Points : 2 241
    Points
    2 241
    Par défaut
    Merci merci, c'est génial tout ça Sa va me donner de quoi tafer un peu

    Je vous remercie infiniement!

  20. #20
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 279
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 279
    Points : 11 015
    Points
    11 015
    Par défaut
    Dans la FAQ, § sur le RAII, tu as un lien vers un chapitre de C++ in Action. Côté explications il est très bien. Côté vocabulaire, on parle de RAII, et côté application, cf la FAQ.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. new anonyme et fuite mémoire
    Par Darz4 dans le forum Langage
    Réponses: 4
    Dernier message: 20/12/2012, 23h28
  2. [tomcat][memoire] java.net.URL et fuite mémoire
    Par Seiya dans le forum Tomcat et TomEE
    Réponses: 6
    Dernier message: 09/03/2009, 10h41
  3. Outil de recherche de fuite mémoire
    Par eag35 dans le forum MFC
    Réponses: 4
    Dernier message: 02/02/2005, 12h46
  4. [SWT]SWT et fuite mémoire(ou pas)
    Par menuge dans le forum SWT/JFace
    Réponses: 2
    Dernier message: 22/06/2004, 21h40
  5. [debug] fuites mémoires
    Par tmonjalo dans le forum C
    Réponses: 3
    Dernier message: 28/07/2003, 17h20

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