Le code ci-dessous provoquera-t-il une fuite de mémoire dans c ++?

class someclass {}; class base { int a; int *pint; someclass objsomeclass; someclass* psomeclass; public: base() { objsomeclass = someclass(); psomeclass = new someclass(); pint = new int(); throw "constructor failed"; a = 43; } } int main() { base temp(); } 

Dans le code ci-dessus, le constructeur jette. Quels objects vont fuir et comment éviter les memory leaks?

 int main() { base *temp = new base(); } 

Que diriez-vous du code ci-dessus? Comment éviter les memory leaks après le lancement du constructeur?

Oui, il y aura une fuite de mémoire. Lorsque le constructeur lance, aucun destructeur ne sera appelé (dans ce cas, vous ne montrez pas de destructeur qui libère les objects alloués dynamicment, mais supposons que vous en avez un).

C’est une raison majeure pour utiliser des pointeurs intelligents – étant donné que les pointeurs intelligents sont des objects à part entière, ils obtiennent des destructeurs appelés pendant le dépilage de la stack de l’exception et ont la possibilité de libérer de la mémoire.

Si vous utilisez quelque chose comme le modèle scoped_ptr <> de Boost, votre classe pourrait ressembler davantage à:

 class base{ int a; scoped_ptr pint; someclass objsomeclass; scoped_ptr psomeclass; base() : pint( new int), objsomeclass( someclass()), psomeclass( new someclass()) { throw "constructor failed"; a = 43; } } 

Et vous n’auriez aucune fuite de mémoire (et le serveur par défaut nettoyerait également les allocations de mémoire dynamics).


Pour résumer (et j’espère que cela répond aussi à la question sur le

 base* temp = new base(); 

déclaration):

Lorsqu’une exception est générée à l’intérieur d’un constructeur, vous devez prendre en compte plusieurs éléments pour gérer correctement les affectations de ressources pouvant avoir eu lieu dans la construction abandonnée de l’object:

  1. le destructeur de l’object en construction ne sera pas appelé.
  2. destructeurs pour les objects membres contenus dans la classe de cet object seront appelés
  3. la mémoire de l’object en construction sera libérée.

Cela signifie que si votre object possède des ressources, vous avez 2 méthodes disponibles pour nettoyer ces ressources qui ont peut-être déjà été acquises lorsque le constructeur lance:

  1. attrapez l’exception, libérez les ressources, puis rallumez-les. Cela peut être difficile à corriger et peut devenir un problème de maintenance.
  2. utilisez des objects pour gérer les durées de vie des ressources (RAII) et utilisez-les comme membres. Lorsque le constructeur de votre object lève une exception, les objects membres se verront appeler des destructeurs et auront la possibilité de libérer la ressource dont ils sont responsables de la durée de vie.

Les deux nouvelles seront fuites.

Atsortingbuez l’adresse des objects créés par le tas à des pointeurs intelligents nommés, de manière à ce qu’ils soient supprimés à l’intérieur du destructeur de pointeurs intelligents qui reçoit un appel lorsque l’exception est levée ( RAII ).

 class base { int a; boost::shared_ptr pint; someclass objsomeclass; boost::shared_ptr psomeclass; base() : objsomeclass( someclass() ), boost::shared_ptr psomeclass( new someclass() ), boost::shared_ptr pint( new int() ) { throw "constructor failed"; a = 43; } }; 

Désormais, les destructeurs psomeclass & pint seront appelés lorsque la stack sera déroulée lorsque l’exception sera levée dans le constructeur, et ces destructeurs désalloueront la mémoire allouée.

 int main(){ base *temp = new base(); } 

Pour une allocation de mémoire ordinaire utilisant new (non implicitement) new, la mémoire allouée par l’opérateur new est automatiquement libérée si le constructeur lève une exception. En ce qui concerne la libération des membres individuels (en réponse aux commentaires sur la réponse de Mike B), la libération automatique ne s’applique que lorsqu’une exception est levée dans le constructeur d’un object nouvellement alloué, et non dans d’autres cas. De plus, la mémoire libérée est celle allouée pour les membres de l’object, pas la mémoire allouée dans le constructeur. En d’autres termes , la mémoire des variables membres a , pint , objsomeclass et psomeclass serait libérée , mais pas la mémoire allouée par new someclass () et new int () .

Je crois que la première réponse est fausse et qu’il y aurait encore une fuite de mémoire. Le destructeur des membres de la classe ne sera pas appelé si le constructeur lève une exception (car il n’a jamais terminé son initialisation et certains membres n’ont peut-être jamais atteint leurs appels de constructeur). Leurs destructeurs ne sont appelés que pendant l’appel de destructeur de la classe. Cela n’a de sens que.

Ce programme simple le démontre.

 #include  class A { int x; public: A(int x) : x(x) { printf("A constructor [%d]\n", x); } ~A() { printf("A destructor [%d]\n", x); } }; class B { A a1; A a2; public: B() : a1(3), a2(5) { printf("B constructor\n"); throw "failed"; } ~B() { printf("B destructor\n"); } }; int main() { B b; return 0; } 

Avec la sortie suivante (en utilisant g ++ 4.5.2):

 A constructor [3] A constructor [5] B constructor terminate called after throwing an instance of 'char const*' Aborted 

Si votre constructeur échoue à mi-parcours, il vous incombe de le gérer. Pire encore, l’exception peut être levée du constructeur de votre classe de base! La manière de traiter ces cas consiste à utiliser un “bloc de tentative de fonction” (mais même dans ce cas, vous devez coder soigneusement la destruction de votre object partiellement initialisé).

L’approche correcte de votre problème serait alors quelque chose comme ceci:

 #include  class A { int x; public: A(int x) : x(x) { printf("A constructor [%d]\n", x); } ~A() { printf("A destructor [%d]\n", x); } }; class B { A * a1; A * a2; public: B() try // <--- Notice this change : a1(NULL), a2(NULL) { printf("B constructor\n"); a1 = new A(3); throw "fail"; a2 = new A(5); } catch ( ... ) { // <--- Notice this change printf("B Cleanup\n"); delete a2; // It's ok if it's NULL. delete a1; // It's ok if it's NULL. } ~B() { printf("B destructor\n"); } }; int main() { B b; return 0; } 

Si vous l'exécutez, vous obtiendrez le résultat attendu dans lequel seuls les objects alloués sont détruits et libérés.

 B constructor A constructor [3] B Cleanup A destructor [3] terminate called after throwing an instance of 'char const*' Aborted 

Vous pouvez toujours vous en servir avec des pointeurs partagés intelligents si vous le souhaitez, avec une copie supplémentaire. Ecrire un constructeur similaire à ceci:

 class C { std::shared_ptr a1; std::shared_ptr a2; public: C() { std::shared_ptr new_a1(new someclass()); std::shared_ptr new_a2(new someclass()); // You will reach here only if both allocations succeeded. Exception will free them both since they were allocated as automatic variables on the stack. a1 = new_a1; a2 = new_a2; } } 

Bonne chance, Tzvi.

Si vous ajoutez un constructeur, vous devez nettoyer tout ce qui est arrivé avant l’appel à lancer. Si vous utilisez l’inheritance ou jetez un destructeur, vous ne devriez vraiment pas l’être. Le comportement est étrange (mon standard n’est pas à scope de main, mais il est peut-être indéfini?).

Oui, ce code perdra de la mémoire. Les blocs de mémoire alloués avec “new” ne sont pas libérés lorsqu’une exception est déclenchée. Cela fait partie de la motivation derrière RAII .

Pour éviter la fuite de mémoire, essayez ce qui suit:

 psomeclass = NULL; pint = NULL; /* So on for any pointers you allocate */ try { objsomeclass = someclass(); psomeclass = new someclass(); pint = new int(); throw "constructor failed"; a = 43; } catch (...) { delete psomeclass; delete pint; throw; } 

Tout ce que vous “nouveau” doit être supprimé, sinon vous causerez une fuite de mémoire. Donc, ces deux lignes:

 psomeclass = new someclass(); pint = new int(); 

Provoquera des memory leaks, car vous devez faire:

 delete pint; delete psomeclass; 

Dans un bloc enfin pour éviter les fuites.

Aussi, cette ligne:

 base temp = base(); 

Est inutile. Vous devez juste faire:

 base temp; 

L’ajout du “= base ()” n’est pas nécessaire.

vous devez supprimer psomeclass … Il n’est pas nécessaire de nettoyer l’entier …

RWendi