64 votes

Capturer une exception par référence est-il dangereux?

Veuillez prendre un coup d'oeil à l'exception lancer et attraper:

void some_function() {
    throw std::exception("some error message");
}

int main(int argc, char **argv) {
    try {
        some_function();
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        exit(1);
    }
    return 0;
}

Est-il sûr d'attraper l'exception lancée par référence?

Ma préoccupation est parce que l'exception e est en fait placée sur la pile de some_function(). Mais some_function() vient de rentrer, provoquant e à être détruits. Donc en fait maintenant e de points à un détruits objet.

Qui est ma préoccupation correct?

Quelle est la bonne façon de transmettre l'exception sans la copie par valeur? Dois-je jeter new std::exception() il est placé dans la dynamique de la mémoire?

96voto

Tony D Points 43962

Il est en effet fort - et a recommandé à l'attraper par const de référence.

"e est en fait placé sur la pile de some_function()"

Non ce n'est pas... l'objet générée est créé dans une quelconque zone de mémoire réservée pour l'utilisation par l'exception d'un mécanisme de gestion:

[exception.jetez] 15.1/4: La mémoire de l'objet exception est affecté d'une quelconque manière, sauf comme indiqué dans 3.7.4.1. L'exception l'objet est détruit après le dernier restant actif gestionnaire pour l'exception des sorties par tous les moyens autres que le renvoi, ou le dernier objet de type std::exception_ptr (18.8.5) qui fait référence à l'objet de l'exception est détruite, si elle est postérieure.

Si une variable locale est spécifié à l' throw, c'est du copié-à-la si nécessaire (l'optimiseur peut être en mesure de créer directement dans ce mémoire). C'est pourquoi...

15.1/5 Lors de la levée de l'objet est un objet de classe, le constructeur sélectionné pour la copie de l'initialisation et le destructeur doit être accessible, même si le copier/déplacer l'opération est élidée (12.8).


Si ce n'est pas cliqué, il pourrait aider à imaginer la mise en œuvre vaguement comme ceci:

// implementation support variable...
thread__local alignas(alignof(std::max_align_t))
    char __exception_object[EXCEPTION_OBJECT_BUFFER_SIZE];

void some_function() {
    // throw std::exception("some error message");

    // IMPLEMENTATION PSEUDO-CODE:
    auto&& thrown = std::exception("some error message");
    // copy-initialise __exception_object...
    new (&__exception_object) decltype(thrown){ thrown };
    throw __type_of(thrown);
    // as stack unwinds, _type_of value in register or another
    // thread_local var...
}

int main(int argc, char **argv)
{
    try {
        some_function();
    } // IMPLEMENTATION:
      // if thrown __type_of for std::exception or derived...
      catch (const std::exception& e) {
        // IMPLEMENTATION:
        // e references *(std::exception*)(&__exception_object[0]);
        ...
    }
}

22voto

Hurkyl Points 1718

Vous devez attraper par référence, sinon vous ne pourriez pas obtenir le type dynamique correct de l'objet. Quant à sa durée de vie, la norme garantit, en [except.throw] ,

L'objet exception est détruit après que le dernier gestionnaire actif restant pour l'exception se termine par un autre moyen que le renvoi, ou le dernier objet de type std :: exception_ptr (18.8.5) qui fait référence à l'objet exception est détruit, selon la date la plus récente.

18voto

Richard Hodges Points 1972

La chasse par référence const est exactement comment les exceptions doivent être pris. L'objet de l'exception n'est pas nécessairement direct "sur la pile". Le compilateur est responsable de la magie appropriée pour faire ce travail.

D'autre part, votre exemple ne peut pas compiler depuis std::exception ne peut être par défaut construits ou copier-construit. Dans ce cas, l' what() méthode retourne un pointeur vers un vide (style c) de la chaîne, ce qui n'est pas particulièrement utile.

Vous suggérons de jeter un std::runtime_error ou std::logic_error selon le cas, ou une classe qui en sont dérivés:

  • logic_error lorsque l'appelant a demandé quelque chose en dehors des paramètres de conception de votre service.
  • runtime_error lorsque l'appelant a demandé quelque chose de raisonnable, mais des facteurs externes vous empêcher d'honorer la demande.

http://en.cppreference.com/w/cpp/error/exception

8voto

Sigismondo Points 2042

De l'exception.jeter:

Lancer une exception de copie initialise (8.5, 12.8) un objet temporaire, appelé l'objet de l'exception. Le temporaire est une lvalue et est utilisé pour initialiser la variable déclarée dans le gestionnaire correspondant (15.3). Si le type de l'objet de l'exception serait incomplète type ou d'un pointeur vers un type incomplète, à l'exception, éventuellement, de cv qualifiés) void le programme est mal formé.

C'est la loi de jeter l'exception des copies l'objet de l'exception dans les exceptions de la zone, à l'extérieur de la pile. Il est donc parfaitement légitime, et même recommandé, à l'exception de catch par référence, puisque l'objet de l'exception durée de vie sera prolongée jusqu'à la dernière catch().

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