2009-09-26 13 views
19

imaginar dos piezas similares de código:¿Cuál es la diferencia entre throw y throw con arg de excepción atrapada?

try { 
    [...] 
} catch (myErr &err) { 
    err.append("More info added to error..."); 
    throw err; 
} 

y

try { 
    [...] 
} catch (myErr &err) { 
    err.append("More info added to error..."); 
    throw; 
} 

Son éstos efectivamente el mismo o en qué se diferencian de alguna manera sutil? Por ejemplo, ¿el primero hace que se ejecute un constructor de copia mientras que, quizás, el segundo reutiliza el mismo objeto para volver a lanzarlo?

Respuesta

26

Dependiendo de cómo se ha organizado su jerarquía de excepciones, vuelva a lanzar una excepción al nombrar la variable de excepción en la sentencia throw puede rebanada el objeto excepción original.

Una expresión tiro sin argumentos arrojará el objeto de excepción actual conservando su tipo dinámico, mientras que una expresión de tiro con un argumento lanzará una nueva excepción basada en la estática tipo del argumento a throw.

E.g.

int main() 
{ 
    try 
    { 
     try 
     { 
      throw Derived(); 
     } 
     catch (Base& b) 
     { 
      std::cout << "Caught a reference to base\n"; 
      b.print(std::cout); 
      throw b; 
     } 
    } 
    catch (Base& b) 
    { 
     std::cout << "Caught a reference to base\n"; 
     b.print(std::cout); 
    } 

    return 0; 
} 

Como se ha dicho anteriormente, el programa sería:

Caught a reference to base 
Derived 
Caught a reference to base 
Base

Si el throw b es reemplazar con un throw, entonces la captura externa también coger el lanzado originalmente Derived excepción. Esto todavía se cumple si la clase interna capta la excepción Base por valor en lugar de referencia, aunque, naturalmente, esto significaría que el objeto de excepción original no se puede modificar, por lo que cualquier cambio a b no se reflejaría en la excepción Derived atrapada por el bloque externo .

+1

¡Ah, me olvidé por completo de rebanar! ¡Maldita sea, eso es importante! Gracias por mencionar esto. +1 (Aunque creo que cuando escribiste "... preservando el tipo estático original ..." querías decir el tipo _dynamic_. Lo que se llama _tynamic type_, después de todo, si no el _ "original static type" _.) - – sbi

+1

Great respuesta, me olvidé completamente de eso también. – GManNickG

+0

Estoy feliz de que alguien más se haya encontrado con el problema _slicing_;) –

16

En el segundo caso de acuerdo con C++ estándar 15,1/6 constructor de copia no se utiliza:

Un saque expresión sin operandos relanzamientos la excepción siendo manejado. La excepción se reactiva con el temporal existente; no se crea un nuevo objeto de excepción temporal. La excepción ya no se considera capturada; por lo tanto, el valor de uncaught_exception() volverá a ser verdadero.

En el primer caso nuevo excepción será lanzado de acuerdo con 15.1/3:

Un saque expresión inicializa un objeto temporal, llamado el objeto de excepción, el tipo de que se determina por la eliminación de cualquier cv-qualifiers de nivel superior del tipo estático del operando de throw y ajustando el tipo de "array of T" o "function returning T" a "puntero a T" o "puntero a función que devuelve T", respectivamente. < ...> El temporal se utiliza para inicializar la variable nombrada en el controlador coincidente (15.3). El tipo de la expresión de proyección no debe ser un tipo incompleto , o un puntero o referencia a un tipo incompleto, que no sea void *, const void *, volatil void * o const volatile void *. Excepto por estas restricciones y las restricciones de coincidencia de tipo mencionadas en 15.3, el operando de lanzamiento se trata exactamente como un argumento de función en una llamada (5.2.2) o el operando de una declaración de retorno. Se requiere

En ambos casos constructor de copia en la fase de tiro (15.1/5):

Cuando el objeto lanzado es un objeto de clase, y el constructor de copia se utiliza para inicializar la copia temporal no es accesible , el programa está mal formado (incluso cuando el objeto temporal podría ser eliminado de otro modo). De forma similar, si el destructor para ese objeto no es accesible, el programa está mal formado (incluso cuando el objeto temporal podría ser eliminado de otro modo).

+0

Como el copiador debe estar accesible para la excepción lanzada originalmente de todos modos, no creo que sea un problema en este caso. Pero la tuya sigue siendo una buena respuesta. +1 – sbi

+0

Sí, pero en el primer caso, copiar c-tor se usará dos veces. –

+1

Dice cuando la declaración especifica un tipo de clase. Sin embargo, en su caso, especifica un tipo de referencia :) La referencia se vinculará directamente y se referirá al objeto de excepción. Por lo tanto, necesitaremos un copiador solo en el momento de tirar, sin capturar en este caso (sería una diferencia cuando un constructor de copia base está protegido, por ejemplo). –

Cuestiones relacionadas