37 votes

Exception dans le destructeur C++

Je suis bien conscient du fait qu'il ne faut pas lancer d'exception dans le destructeur.

Mais pour me faire une idée de ce concept, j'ai codé cet exemple :-.

#include <iostream>
using namespace std;

class A {
private: 
    int i;

public:
    A()
    {
        i = 10;
    }
    ~A()
    {
        throw 30;
    }
};
int main(){
    try{
        A();
        throw 10;
    }
    catch (int i){
        cout << i << endl;
        cout << "exception caught" << endl;
    }
}

D'après ce que j'ai compris, ce programme devrait être terminé en appelant std::terminate() car il y aura deux exceptions en même temps. Mais, ce programme donne la sortie suivante : -.

30
exception caught

Quelqu'un peut-il m'expliquer la logique qui sous-tend cette situation et pourquoi elle ne se termine pas ?

13 votes

Pourquoi throw 10; est censé être exécuté ?

3 votes

Impossible de reproduire G++ 5.1.0 : "terminate called after throwing an instance of 'int'" (terminaison appelée après avoir lancé une instance de 'int')

0 votes

Il est bon de savoir que vous ne devez pas lancer d'exception dans le destructeur, mais savez-vous pourquoi ?

69voto

Vittorio Romeo Points 2559

std::terminate sera appelé si une exception est levée lors de l'opération déroulement de la pile . Cela signifie que si une exception est appelée pendant qu'une autre exception est traitée entonces std::terminate sera appelé.

Dans votre exemple, ce n'est pas le cas - A(); construira et détruire immédiatement une instance de A . Le site throw 30 seront alors capturés correctement.

En changeant votre code pour :

int main(){
    try{
        A a;      // begin `a` lifetime 
        throw 10; // | throw #0           
                  // | end `a` lifetime   
                  // throw #1
    }
    catch(int i){
        cout<<i<<endl;
        cout<<"exception caught"<<endl;
    }
}

garantira que std::terminate sera appelé. Dans ce cas, a sera détruit et jettera pendant qu'une autre exception est traitée .

exemple de coliru en direct


Informations complémentaires :


Notez que en C++11 et plus votre extrait de code appellera std::terminate et vous donner un avertissement :

main.cpp : Dans le destructeur 'A::~A()' :

main.cpp:16:15 : warning : throw appellera toujours terminate() [-Wterminate]

     throw 30;

           ^~

main.cpp:16:15 : note : en C++11 les destructeurs sont par défaut noexcept

est appelé après avoir lancé une instance de 'int'.

bash : ligne 7 : 1505 Aborted (core dumped) ./a.out

Comme on le voit dans la sortie du compilateur, depuis C++11 les destructeurs sont implicitement noexcept(true) . Si vous voulez empêcher ce comportement, vous pouvez simplement les marquer comme noexcept(false) . Exemple :

~A() noexcept(false)
{
    throw 30;
}

exemple concret sur coliru

0 votes

En fait, std::terminate appelé avec une variable non nommée également. D'après iostream.h Le PO utilise un ancien compilateur

1 votes

@Slava : c'est un problème sans rapport. En C++11, les destructeurs sont implicitement noexcept . Je modifierai ma réponse.

10voto

EyasSH Points 3030

Dans votre exemple, A() construire une variable temporaire pour A puis le détruit immédiatement. Ainsi, throw 10; n'est jamais exécuté.

El throw a lieu est dans le destructeur de A . Lors de l'exécution A::~A() le programme n'est pas en train de se dérouler (c'est-à-dire de nettoyer l'état d'une exception) à ce moment-là. Voir "Destructeurs qui lancent" par exemple.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X