7
votes

La mémoire est-elle libérée lorsque je jette une exception?

Je débattais avec certains collègues sur ce qui se passe lorsque vous lancez une exception dans une classe allouée de manière dynamique. Je sais que malloc code> est appelé, puis le constructeur de la classe. Le constructeur ne revient jamais, alors qu'advient-il du masloc code>?

Considérez l'exemple suivant: P>

class B
{
public:
    B()
    {
        cout << "B::B()" << endl;
        throw "B::exception";
    }

    ~B()
    {
        cout << "B::~B()" << endl;          
    }
};

void main()
{
    B *o = 0;
    try
    {
        o = new B;
    }
    catch(const char *)
    {
        cout << "ouch!" << endl;
    }
}


1 commentaires

Réponse courte: oui. Voir Cătălin Pitiş Répondre pour plus de détails.


4 Réponses :


6
votes

Lorsqu'une exception est lancée du constructeur, la mémoire allouée par nouveau est libérée, mais le destructeur de la classe B n'est pas appelé.


3 commentaires

Oui. Juste pour compléter votre réponse ... Il y a une bonne raison pour que le destructeur ne soit pas appelé: l'objet n'a jamais été créé. Les destructeurs sont appelés uniquement pour des objets existants dans une période donnée. Et ce sont les cas où le constructeur s'est terminé avec succès.


Oui tu as raison. Un objet est considéré comme créé (donc destructible) qu'après exécuté le constructeur exécuté.


Remarque: Les destrucotr de tout élément et classe de base entièrement construits sont appelés.



10
votes

Un appel à

new B();
  • allouant avec un opérateur nouveau () (Soit le global ou une classe spécifique, potentiellement un placement avec la syntaxe nouveau (xxx) B () code>) li>
  • appeler le constructeur. LI> ul>

    Si le constructeur jette, l'opérateur correspondant supprime est appelé. Le cas où la suppression correspondante est un placement que l'on est le seul cas où un opérateur de suppression de placement est appelé sans la syntaxe :: opérateur Suppr (). Supprimer x; code> ou Suppr [] x; code> Ne appelez pas les opérateurs de suppression de placement et il n'y a pas de syntaxe similaire au placement nouveau pour les appeler. P>

    Notez que, tandis que le destructeur de B sera pas strong> être appelé, des sous-observations déjà construites (membres ou classes B et B) seront désignées avant l'appel à l'opérateur Supprimer. Le constructeur qui n'est pas appelé est celui pour b. P> p>


5 commentaires

Addendum important: le constructeur de B n'a pas fonctionné, il n'y avait jamais eu d'objet de ce type. S'il y avait un nouveau () étant appelé dans le constructeur de B avant que l'exception soit déclenchée, c'est pas étant libéré - et vous n'avez plus de pointeur sur cette mémoire, vous ne pouvez donc pas supprimer () ce. Tenir à l'écart de nouveau () dans les constructeurs.


Si vous utilisez des techniques Raii (par exemple, avec STD :: Auto_PTR), New () dans les constructeurs n'est pas une grosse affaire.


@DevSolar: Tout dépend de l'endroit où le résultat de nouveau est stocké. S'il s'agit d'un pointeur intelligent, d'éther local au constructeur ou d'un membre de la classe B ou de ses parents, cet objet sera détruit et la mémoire allouée dans le constructeur de B, mais avant que le lancement de l'exception ne soit libérée. Évidemment si vous faites de nouveaux foo (); Dressez «bar», la mémoire ne sera pas libérée et le FOO ne sera pas détruit. Dans un constructeur ou ailleurs. Je vais clarifier que le destructeur de B n'est pas appelé.


Si le constructeur jette le destructeur n'est pas (je ne répète pas) appelé. Seuls les membres entièrement construits et toutes les classes de base entièrement construites auront leur constructeur appelé.


@Devsolar: La mémoire est relâchée automatiquement (notez la "nouvelle instruction" [qui appelle le constructeur] ne finit jamais). Si vous utilisiez un pointeur intelligent, cela ne vous aiderait pas au fur et à mesure que le constructeur au pointeur intelligent ne serait jamais entré et ne pourrait donc pas aider dans la distribution.



2
votes

Dans ce cas, votre objet, O, ne sera pas réellement construit et la mémoire allouée par nouveau est libérée. En tant que tel, le destructeur ne s'appelle pas. Donc, vous n'avez pas besoin d'appeler:

delete o;


1 commentaires

Par conséquent, si le constructeur a effectué Certains le travail d'acquisition de ressources avant tout ce qui déclenche l'exception, c'est le travail de ce constructeur à nettoyer avant sa lancement.



1
votes

de la norme C ++ 2003 5.3.4 / 17 - Nouveau:

Si une partie de l'initialisation de l'objet décrite ci-dessus se termine en lançant une exception et une fonction de distribution appropriée peut être trouvée, la fonction de distribution est appelée à libérer la mémoire dans laquelle l'objet était construit, après quoi l'exception continue de se propager. dans le contexte de la nouvelle expression. Si aucune fonction de transaction correspondante sans ambiguïté ne peut être trouvée, la propagation de l'exception ne provoque pas la mémoire de la mémoire de l'objet. [Remarque: Ceci est approprié lorsque la fonction d'allocation appelée n'alloue pas la mémoire; Sinon, il risque d'entraîner une fuite de mémoire. ]

Donc, il peut y avoir une fuite ou non, cela dépend de la présence d'un réparlocator approprié (qui est normalement le cas, à moins que l'opérateur neuf / supprime n'a été remplacé). compilateur est responsable du câblage dans un appel à celui-ci si le constructeur jette.

Notez que cela est plus ou moins indépendant de ce qui arrive aux ressources acquises dans le constructeur, ce qui est la première tentative de réponse discutée - et est une question qui est discutée dans de nombreux FAQ, articles et affichages.


1 commentaires

Cela était vraiment destiné à répondre à la question (duplicate?) Ici: Stackoverflow.com/questions/1674980/...