lanzar nuevo std :: excepción vs lanzar std :: excepción

113

mientras miraba un código, me topé con:

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

y siempre pensé que no necesitas / no deberías usar newaquí.
¿Cuál es la forma correcta? ¿Ambos están bien? Si es así, ¿hay alguna diferencia?

Por cierto, por lo que puedo ver mientras "grepping" con PowerShell boost libs nunca se usan throw new.

PD: también encontré un código CLI que usa throw gcnew. ¿Eso esta bien?

NoSenseEtAl
fuente
1
Creo que throw gcnewsería útil, por ejemplo. si desea que el código administrado detecte su excepción. ¿Alguien puede corregirme en eso?
jpalecek
1
.Net se ocupa de las excepciones mediante puntero, por lo que lanzar gcnew es lo correcto.
Sebastian Redl
1
@SebastianRedl. ¿El "puntero" de red puede ser ambiguo? Aunque gcnew ciertamente no lo es. System::Exceptiones generalmente una referencia a un objeto administrado en el montón recolectado de basura. Siempre he tirado gcnewy atrapado System::Exception ^. Por supuesto, también utilizo finallytodo el tiempo en C ++ / CLI, aunque a menudo no se mezclan con excepciones de C ++ en el mismo trybloque, no estoy seguro de por qué.

Respuestas:

89

La forma convencional de lanzar y capturar excepciones es lanzar un objeto de excepción y capturarlo por referencia (generalmente constreferencia). El lenguaje C ++ requiere que el compilador genere el código apropiado para construir el objeto de excepción y limpiarlo adecuadamente en el momento apropiado.

Lanzar un puntero a un objeto asignado dinámicamente nunca es una buena idea. Se supone que las excepciones le permitirán escribir código más sólido ante condiciones de error. Si lanza un objeto de excepción de la manera convencional, puede estar seguro de que si es capturado por una cláusula catch que nombra el tipo correcto, por a catch (...), ya sea que se vuelva a lanzar o no, se destruirá correctamente en el momento adecuado. (La única excepción es si nunca se detecta, pero esta es una situación no recuperable, se mire como se mire).

Si lanza un puntero a un objeto asignado dinámicamente, debe asegurarse de que, sea cual sea el aspecto de la pila de llamadas en el punto en el que desea lanzar su excepción, hay un bloque de captura que nombra el tipo de puntero correcto y tiene la deletellamada adecuada . Su excepción nunca debe ser capturada a catch (...)menos que ese bloque vuelva a lanzar la excepción que luego es capturada por otro bloque de captura que maneja correctamente la excepción.

Efectivamente, esto significa que ha tomado la función de manejo de excepciones que debería facilitar la escritura de código robusto y dificulta la escritura de código que sea correcto en todas las situaciones. Esto deja de lado el problema de que será casi imposible actuar como código de biblioteca para el código de cliente que no espera esta característica.

CB Bailey
fuente
1
"lanzar un objeto de excepción" apilar o apilar mi amigo? ¿Pila o montón? (Tal vez estaba viendo un mal ejemplo global en alguna parte) oh, y si se apila, ¿cuál es el alcance apropiado?
@ebyrob: No estoy realmente seguro de lo que está preguntando, pero parece que desea saber sobre el almacenamiento y / o la vida útil del objeto de excepción que podría responderse aquí . Si no, es mejor que haga una pregunta aparte.
CB Bailey
31

No es necesario usarlo newal lanzar una excepción.

Solo escribe:

throw yourexception(yourmessage);

y capturar como:

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

Tenga en cuenta que yourexceptiondebe derivar de forma std::exceptiondirecta o indirecta.

Nawaz
fuente
7
¿Por qué? ¿por qué no usar new? por qué derivar yourexceptiona partir std::exception?
Walter
Cuando soy vago (que es muy a menudo), ¿por qué no throw std::exception;funciona? g ++ no parece compilarlo ...
7
@ebyrob: std::exceptiones un tipo, y no puedes lanzar un tipo , tienes que lanzar un objeto . Entonces la sintaxis debería ser esta: throw std::exception();Eso se compilará. Ahora bien, qué tan bueno es eso, es una pregunta completamente diferente.
Nawaz
22

El lanzamiento new std::exceptiones correcto si el sitio de la llamada espera atrapar un std::exception*. Pero nadie esperará captar un puntero a una excepción. Incluso si documentas que eso es lo que hace tu función y las personas leen la documentación, es probable que se olviden e intenten captar una referencia a un std::exceptionobjeto.


fuente
27
El lanzamiento new std::exceptionsolo es correcto si el sitio de llamada espera capturar un puntero Y espera hacerse cargo de la administración de la excepción de asignación Y nunca habrá casos en los que su función sea llamada por algo que no captura explícitamente el puntero correcto ( catch(...)o ningún manejo) de lo contrario habrá una fuga de objetos. En resumen, esto se puede aproximar como "nunca".
CB Bailey
Es curioso cómo se aceptó esta respuesta, cuando en realidad es el comentario de @ CharlesBailey la respuesta correcta.
John Dibling
@John: Eso también se me pasó por la cabeza. Pero creo que el golpe uno-dos tiene un buen efecto conmigo dando un resumen seco y Charles ampliando de manera divertida las diversas formas en que la gente puede olvidarse de tratarlo adecuadamente. Sin embargo, es una lástima que no obtengas reputación de los comentarios con más votos.
Charles no dio su respuesta, y esta A (a diferencia de la otra) tiene explicaciones tanto en la A como en el comentario.
NoSenseEtAl
9

Las preguntas frecuentes de C ++ tienen una buena discusión sobre esto:

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

Básicamente, "a menos que haya una buena razón para no capturar por referencia. Evite capturar por valor, ya que eso hace que se haga una copia y la copia puede tener un comportamiento diferente al que se lanzó. Solo en circunstancias muy especiales debe capturar por puntero". "

user1202136
fuente
2
Como de costumbre, las preguntas frecuentes están mal redactadas. Puede capturar por valor o referencia. Un puntero resulta ser un valor (que captura por valor o referencia). Recuerde que el tipo Aes distinto del tipo, A*así que si lo hago throw A(), NO puedo catch(A* e)entenderlo, ya que es un tipo completamente diferente.
Martin York
Estos enlaces ahora están rotos.
stephenspann
1
Arreglé los enlaces @spanndemic
user1202136
1

El operador nuevo no puede garantizar que nunca generará una excepción. Por esta razón, usarlo para lanzar una excepción "válida" (prevista) produciría un código que no puede garantizarse que no se bloquee. Dado que puede haber solo una excepción a la vez, y su programa intenta lanzar dos antes de que cualquiera de ellas pueda ser detectada, lo mejor que puede hacer una implementación es abortar inmediatamente su programa, por ejemplo, llamando a std :: terminate.

zkoza
fuente