lancer un nouveau std :: exception vs lancer std :: exception

113

en regardant du code, je suis tombé sur:

throw /*-->*/new std::exception ("//...

et j'ai toujours pensé que vous n'avez pas besoin / que vous ne devriez pas utiliser newici.
Quelle est la bonne manière, les deux sont-ils OK, si oui, y a-t-il une différence?

BTW d'après ce que je peux voir en "grepping" avec PowerShell, les bibliothèques boost ne l'utilisent jamais throw new.

PS aussi j'ai trouvé du code CLI qui utilise throw gcnew. Est-ce que ça va?

NoSenseEtAl
la source
1
Je pense que ce throw gcnewserait utile par exemple. si vous voulez que le code managé intercepte votre exception. Quelqu'un peut-il me corriger là-dessus?
jpalecek
1
.Net traite les exceptions par pointeur, alors lancer gcnew est la bonne chose à faire.
Sebastian Redl
1
@SebastianRedl .Net "pointeur" peut être ambigu? Bien que gcnew ne le soit certainement pas. System::Exceptionest généralement une référence à un objet managé sur le tas de récupération de place. J'ai toujours jeté gcnewet attrapé avec System::Exception ^. Bien sûr, j'utilise également finallytout le temps en C ++ / CLI, bien que je ne me mélange pas souvent avec des exceptions C ++ dans le même trybloc, je ne sais pas pourquoi.

Réponses:

89

La manière conventionnelle de lancer et d'intercepter des exceptions est de lancer un objet d'exception et de l'attraper par référence (généralement constréférence). Le langage C ++ nécessite que le compilateur génère le code approprié pour construire l'objet d'exception et le nettoyer correctement au moment opportun.

Lancer un pointeur vers un objet alloué dynamiquement n'est jamais une bonne idée. Les exceptions sont censées vous permettre d'écrire du code plus robuste face aux conditions d'erreur. Si vous lancez un objet d'exception de la manière conventionnelle, vous pouvez être sûr que s'il est intercepté par une clause catch nommant le type correct, par un catch (...), s'il est ensuite relancé ou non, il sera détruit correctement au moment approprié. (La seule exception étant si elle n'est jamais interceptée du tout mais c'est une situation irrécupérable quelle que soit la façon dont vous la regardez.)

Si vous lancez un pointeur vers un objet alloué dynamiquement, vous devez être sûr que quel que soit l'aspect de la pile d'appels au moment où vous souhaitez lever votre exception, il existe un bloc catch qui nomme le type de pointeur correct et a l' deleteappel approprié . Votre exception ne doit jamais être interceptée à catch (...)moins que ce bloc ne relance l'exception qui est ensuite interceptée par un autre bloc catch qui traite correctement l'exception.

En fait, cela signifie que vous avez utilisé la fonction de gestion des exceptions qui devrait faciliter l'écriture de code robuste et rendu très difficile l'écriture de code correct dans toutes les situations. Cela laisse de côté le problème qu'il sera presque impossible d'agir en tant que code de bibliothèque pour le code client qui n'attendra pas cette fonctionnalité.

CB Bailey
la source
1
"jeter un objet d'exception" pile ou entasser mon ami? Pile ou tas? (Peut-être que je regardais un mauvais exemple global quelque part) oh et si pile, quelle est la portée appropriée?
@ebyrob: Je ne suis pas vraiment sûr de ce que vous demandez, mais il semble que vous souhaitiez en savoir plus sur le stockage et / ou la durée de vie de l'objet d'exception auquel vous pourriez répondre ici . Sinon, vous feriez peut-être mieux de poser une question distincte.
CB Bailey
31

Pas besoin d'utiliser newlors de la levée d'exception.

Ecrivez:

throw yourexception(yourmessage);

et attraper comme:

catch(yourexception const & e)
{
      //your code (probably logging related code)
}

Notez que cela yourexceptiondoit dériver std::exceptiondirectement ou indirectement.

Nawaz
la source
7
Pourquoi? pourquoi ne pas l'utiliser new? Pourquoi tirer yourexceptionde std::exception?
Walter
Quand je suis paresseux (ce qui est souvent trop souvent), pourquoi ne throw std::exception;fonctionne pas ? g ++ ne semble pas le compiler ...
7
@ebyrob: std::exceptionest un type, et vous ne pouvez pas lancer un type , vous devez lancer un objet . La syntaxe devrait donc être la suivante: throw std::exception();cela compilera. Maintenant, à quel point c'est bon, est une question complètement différente.
Nawaz
22

Le lancement new std::exceptionest correct si le site d'appel s'attend à attraper un fichier std::exception*. Mais personne ne s'attendra à attraper un pointeur vers une exception. Même si vous documentez ce que fait votre fonction et que les gens lisent la documentation, ils sont toujours susceptibles d'oublier et d'essayer d'attraper une référence à un std::exceptionobjet à la place.


la source
27
Lancer new std::exceptionn'est correct que si le site d'appel s'attend à attraper un pointeur ET s'attend à prendre en charge la gestion de l'exception d'allocation ET il n'y aura jamais de cas où votre fonction sera appelée par quelque chose qui n'attrape pas explicitement le bon pointeur ( catch(...)ou aucune manipulation du tout) sinon il y aura une fuite d'objet. En bref, cela peut être approximé comme "jamais".
CB Bailey
Il est curieux de savoir comment cette réponse a été acceptée, alors qu'en réalité c'est le commentaire de @ CharlesBailey qui est la bonne réponse.
John Dibling
@John: Cela m'a aussi traversé l'esprit. Mais je pense que le coup de poing un-deux a un bon effet avec moi donnant le résumé sec et Charles développant de manière amusante sur les différentes façons dont les gens sont susceptibles d'oublier de le gérer correctement. Dommage que vous n'obteniez pas la réputation des commentaires votés à la hausse, cependant.
Charles n'a pas fourni sa réponse, et ce A (contrairement à l'autre) a des explications à la fois dans le A et dans le commentaire.
NoSenseEtAl
9

La FAQ C ++ a une belle discussion à ce sujet:

  1. https://isocpp.org/wiki/faq/exceptions#what-to-catch
  2. https://isocpp.org/wiki/faq/exceptions#catch-by-ptr-in-mfc

Fondamentalement, "à moins qu'il n'y ait une bonne raison de ne pas le faire, attraper par référence. Évitez d'attraper par valeur, car cela entraîne une copie et la copie peut avoir un comportement différent de celui qui a été lancé. Ce n'est que dans des circonstances très spéciales que vous devez attraper par pointeur. "

user1202136
la source
2
Comme d'habitude, la FAQ est mal rédigée. Vous pouvez attraper par valeur ou référence. Un pointeur se trouve être simplement une valeur (que vous attrapez par valeur ou référence). Rappelez-vous que le type Aest distinct du type, A*donc si je le fais, throw A()je ne peux PAS attraper catch(A* e)car il est de type complètement différent.
Martin York
Ces liens sont maintenant rompus.
stephenspann
1
J'ai corrigé les liens @spanndemic
user1202136
1

L'opérateur new ne peut garantir qu'il ne déclenchera jamais d'exception. Pour cette raison, l'utiliser pour lancer une exception "valide" (intentionnelle) produirait un code dont on ne peut garantir qu'il ne plantera pas. Puisqu'il ne peut y avoir qu'une seule exception à la fois et que votre programme essaie d'en lancer deux avant qu'aucune d'entre elles ne puisse être interceptée, la meilleure chose qu'une implémentation puisse faire est d'abandonner immédiatement votre programme, par exemple en appelant std :: terminate.

zkoza
la source