Forum Programmation.c++ Constructeur, destructeur, et autre...

Posté par  .
Étiquettes : aucune
0
15
juin
2007
Bonjour à tous,
je vais peut-être passer pour un imbécile, mais je n'arrive pas vraiment à comprendre la gestion de la mémoire dynamique.
Supposons la fonction f1:
void f1()
{
    Objet* obj= new Objet();
    obj->print();
    // et à la fin on désalloue:
    delete obj;
}

maitenant, regardons la fonction f2 qui (presque) la même chose:
void f2()
{
    Objet obj();
    obj.print();
}

Maintenant, mes questions:
* Comment se fait-il que dans un cas, je suis obligé d'appeler l'opérateur delete, et pas dans l'autre?
* Comment est désalloué l'objet obj dans f2?
* A quoi pourrait bien servir de ne pas désallouer obj dans f1 puisque f1 ne retourne rien?

Voilà, je vous remercie d'avance pour vos éclaircissements... (et soyez indulgents, le développement n'est pas mon métier!)

--
Gilles
  • # la fonction f1 devrait s'écrire comme f2

    Posté par  . Évalué à 4.

    Dans ce context la le new est inutile. Par contre dans le cas où tu as besoin d'un objet que doit durer plus longtemps que ta fonction le new devient nécessaire ( sinon il est détruit en fin de bloc ( {} )

    un new alloue de la mémoire, qui ne sera pas désaloué automatiquement, donc cela entraine forcément un delete plus tard.

    Il ne faut pas décorner les boeufs avant d'avoir semé le vent

    • [^] # Re: la fonction f1 devrait s'écrire comme f2

      Posté par  . Évalué à 1.

      Ok!!
      Donc si je fais:
      Objet* f1()
      {
          Objet* obj= new Objet();
          obj->print();
          // et à la fin on désalloue:
          return obj;
      }
      Je suppose que ça fonctionne correctement.
      Par contre si je fais:
      Objet* f2()
      {
          Objet obj();
          obj.print();
          return &obj;
      }
      C'est une erreur.
  • # Hum

    Posté par  (site web personnel) . Évalué à 2.

    Normalement tes ressources allouées sont automatiquement détruite a la fin de l'exécution de la fonction.

    A savoir :
    void fonction()
    {
    char tab[5] = ['a','b','c','d','e'];
    }

    Le tableau sera vidé a la fin de l'exécution de la fonction.

    Après tu as le cas de des objets.

    Quand tu utilises ton new, tu instancies ton objet (la place mémoire est prise), une fois que tu quitte la fonction il devrais être dé-alloué automatiquement.

    En fait le delete explicite va libérer la mémoire pour cet objet là où tu l'appelles.

    Bon je suis plus trop sur du fait que tu doives faire un appel explicite a ton delete dans une fonction pour dé-allouer l'objet.
    (Je sort de trop de php pour te répondre)

    Mais c'est le même soucis que ton allocation de zone mémoire via un appel malloc, tu alloues ta zone mémoire, ensuite tu dois la détruire, sinon c'est une fuite de mémoire.
    (en fait a la fin du programme elle sera libérée de toute façon).

    Je te conseille d'aller relire le comportement des fonctions en C++ de plus prêt, tu trouvera une réponse a ce problème sure.
    • [^] # Re: Hum

      Posté par  (site web personnel) . Évalué à 4.

      Je viens de relire tes fonctions plus sérieusement, maintenant j'ai ta réponse.

      Dans le cas 1 tu alloue un objet et fait pointer un pointeur dessus.
      A la fin de ta fonction le pointeur sera détruit tout seul (variable locale), mais pas ton objet qui a été aloué.

      Dans le cas 2 tu crée ton objet localement, ton obj est une variable locale (comme un int, float, string, char, etc) et sera libéré tout seul a la fin de la fonction.
  • # Bien différencier C++ et Java !

    Posté par  . Évalué à 3.

    void f2()
    {
    Objet obj (); /* <- NE PAS METTRE DE PARENTHESES ! */
    obj.print();
    }

    En tout cas, pas par défaut. De la même façon que tu vas faire un int x, tu va faire un Object obj;. Type et nom de variable.

    Le fait que tu puisse mettre des parenthèses te permet de choisir le constructeur qui doit être appelé, quand il y en a plusieurs.


    Quand tu crées une variable en C, il reserve de la place dans la pile, en plus de ce qu'il y met déjà (sauvegarde des registres, adresse de retour et paramètres de la fonction). Eventuellement, il peut affecter une valeur par défaut à la place réservée.C'est d'ailleurs pour cette raison que C t'impose de déclarer toutes tes variables en début de bloc : spécialement parce que ce n'est PAS dynamique, mais déterminé au moment de la compilation (ce qui, en soi, est génial).

    D'ailleurs, message perso aux afficionados de la qualité du code : il y a certains chefs de projets qui mettent un point d'honneur à vérifier que toutes les boucles et opérations conditionnelles (if while etc ...) soit bien encadrées dans des accolades. On leur répondra que 1) Si le langage permet certaines facilités, c'est pour des raisons précises 2) les accolades en C sont pratiquement toujours synonymes d'ouverture d'un nouveau cadre de pile et que la plupart des gens ne le savent plus 3) laissez les coders faire leur boulot :-)

    La réservation de cette place ce fait d'ailleurs simplement en décalant le pointeur de pile de n octets. En C++, il faudra peut-être appeler une fonction plus sophistiquée pour mettre ton objet au propre : le constructeur. Celui-ci est donc appelé automatiquement en lui passant l'adresse de la mémoire fraîchement allouée.

    Lorsque ta fonction se termine, en C, pour libérer la mémoire dans la pile, il suffit de faire remonter le pointeur de pile et de laisser tout le reste en l'état. En C++, il y a aura peut-être des ressources internes à libérer, donc les destructeurs seront appelés automatiquement aussi, mais fondamentalement, la mémoire allouée est la même qu'en C.


    new et delete maintenant, c'est exactement la même chose que malloc et free en C. Ca sert à faire une allocation explicite d'un objet, et ne le libérer que lorsque le programmeur le décide explicitement. Et la, ça fonctionne aussi comme en C : une zone mémoire réservée, distincte de la pile, avec un tas, une table d'allocation, et une fragmentation mémoire.
  • # Principes du C++

    Posté par  . Évalué à 1.

    Dans le cas de la fonction f1 :

    Tu déclare un pointeur local (à ta méthode, donc sur la pile) sur un objet de la classe "Objet" il n'est donc pas alloué. Tu dois donc l'allouer explicitement (l'allocation se fera sur le tas cette fois) et le constructeur que tu as spécifié sera appelé : soit ici le constructeur sans paramètre (constructeur par défaut). Tu dois aussi le détruire explicitement et le destructeur de la classe Objet sera appelé (et la mémoire dé-allouée).

    Dans le cas de la fonction f2 :

    Tu déclare un objet de la classe Objet en local de ta méthode (donc sur la pile) : le constructeur par défaut est donc appelé automatiquement. De même lorsque tu sort de ta fonction tous les objets locaux sont détruits (dé-alloués) et leur destructeurs sont appelés.

    Pour répondre à ta dernière question :

    Si tu sort de ta méthode sans delete alors le pointeur qui lui était alloué sur la pile est dépilé et est perdu : tu n'as donc plus aucune référence sur l'objet que tu as alloué. Celui-ci reste en mémoire jusqu'à la fin de ton process. C'est un bel exemple de memory leak.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.