Partager via


Exceptions et déroulement de pile en C++

Dans le mécanisme d'exceptions C++, le contrôle se déplace de l'instruction throw à la première instruction catch qui peut gérer le type levée.Lorsque l'instruction catch est atteinte, toutes les variables automatiques qui sont dans la portée entre le jet et les instructions catch sont perdues dans un processus appelé le déroulement de pile.Dans le déroulement de la pile, l'exécution se poursuit comme suit :

  1. Le contrôle atteint l'instruction d' try par exécution séquentielle normale.La section protégée dans le bloc d' try est exécutée.

  2. Si aucune exception n'est levée pendant l'exécution de la section protégée, les clauses d' catch qui suivent le bloc d' try ne sont pas exécutées.L'exécution continue à l'instruction après la dernière clause d' catch qui suit le bloc associé d' try .

  3. Si une exception est levée pendant l'exécution de la section protégée ou dans toute routine que la section protégée appelle directement ou indirectement, un objet exception est créé à partir de l'objet qui est créé par l'opérande d' throw .(Cela implique qu'un constructeur de copie peut être impliqué.) À ce stade, le compilateur recherche une clause d' catch dans un contexte d'exécution plus élevé qui peut gérer une exception de type qui est levée, ou pour un gestionnaire d' catch qui peut gérer tout type d'exception.Les gestionnaires d' catch sont examinés par ordre d'apparition après le bloc d' try .Si aucun gestionnaire approprié n'est trouvé, le prochain dynamiquement bloc englobant d' try est examiné.Ce processus se poursuit jusqu'à ce que le bloc englobant extérieur de try soit révisé.

  4. Si un gestionnaire correspondant est toujours introuvable, ou si une exception se produit pendant le processus de déroulement mais avant que le gestionnaire est de contrôle, la fonction runtime intégrée terminate est appelée.Si une exception se produit après que l'exception a été levée mais avant que le déroulement démarre, terminate est appelé.

  5. Si un gestionnaire d' catch de correspondance est trouvée, et il intercepte par valeur, son paramètre formel est initialisé en copiant l'objet exception.S'il intercepte par référence, le paramètre est initialisé pour faire référence à l'objet exception.Une fois le paramètre formel initialisation, le processus de déroulement de la pile commence.Cela implique la destruction de tous les objets automatiques qui ont été entièrement construire- mais pas encore détruire- entre le début du bloc d' try associé au gestionnaire d' catch et le site de lever d'exception.La destruction se produit dans l'ordre inverse de la construction.Le gestionnaire d' catch est exécuté et l'exécution des résumés de programme après le dernier dérouleur-, c'est-à-dire à la première instruction ou élément qui n'est pas un gestionnaire d' catch .Le contrôle ne peut présenter un gestionnaire d' catch via une exception levée, jamais dans une instruction d' goto ou une étiquette d' case dans une instruction d' switch .

Exemple de déroulement de pile

L'exemple suivant montre comment la pile est déroulée lorsqu'une exception est levée.L'opération sur le thread branche de l'instruction throw dans C à l'instruction catch dans main, et déroule chaque fonction le long de la route.Notez l'ordre dans lequel les objets d' Dummy sont créés et détruits à mesure qu'ils sont hors de portée.Notez également qu'aucune fonction ne se termine à l'exception main, qui contient l'instruction catch.Fonctionnent A ne retourne jamais de son appel à B(), et B ne retourne jamais de son appel à C().Si vous supprimez les marques de commentaire de la définition du pointeur d' Dummy et de l'instruction correspondante de suppression, puis exécutez le programme, notez que le pointeur n'est jamais supprimé.Cela indique ce qui peut se produire lorsque les fonctions ne fournissent pas garantie d'exception.Pour plus d'informations, consultez procédure : Concevez des exceptions.Si vous supprimez l'instruction catch, vous pouvez observer ce qui se produit lorsqu'un programme se termine en raison d'une exception non gérée.

#include <string>
#include <iostream>
using namespace std;
 
class MyException{};
class Dummy
{
    public:
    Dummy(string s) : MyName(s) { PrintMsg("Created Dummy:"); }
    Dummy(const Dummy& other) : MyName(other.MyName){ PrintMsg("Copy created Dummy:"); }
    ~Dummy(){ PrintMsg("Destroyed Dummy:"); }
    void PrintMsg(string s) { cout << s  << MyName <<  endl; }
    string MyName; 
    int level;
};
 
 
void C(Dummy d, int i)
{ 
    cout << "Entering FunctionC" << endl;
    d.MyName = " C";
    throw MyException();   
 
    cout << "Exiting FunctionC" << endl;
}
 
void B(Dummy d, int i)
{
    cout << "Entering FunctionB" << endl;
    d.MyName = "B";
    C(d, i + 1);   
    cout << "Exiting FunctionB" << endl; 
}
 
void A(Dummy d, int i)
{ 
    cout << "Entering FunctionA" << endl;
    d.MyName = " A" ;
  //  Dummy* pd = new Dummy("new Dummy"); //Not exception safe!!!
    B(d, i + 1);
 //   delete pd; 
    cout << "Exiting FunctionA" << endl;   
}
 
 
int main()
{
    cout << "Entering main" << endl;
    try
    {
        Dummy d(" M");
        A(d,1);
    }
    catch (MyException& e)
    {
        cout << "Caught an exception of type: " << typeid(e).name() << endl;
    }
 
    cout << "Exiting main." << endl;
    char c;
    cin >> c;
}
 
/* Output:
    Entering main
    Created Dummy: M
    Copy created Dummy: M
    Entering FunctionA
    Copy created Dummy: A
    Entering FunctionB
    Copy created Dummy: B
    Entering FunctionC
    Destroyed Dummy: C
    Destroyed Dummy: B
    Destroyed Dummy: A
    Destroyed Dummy: M
    Caught an exception of type: class MyException
    Exiting main.
 
*/