C++ Excepciones preguntas sobre el regreso de la excepción original
exception rethrow (4)
En ambos casos, dado que capta por referencia, está alterando efectivamente el estado del objeto de excepción original (que puede pensar que reside en una ubicación de memoria mágica que permanecerá válida durante el desenrollado posterior - 0x98e7058
en el ejemplo a continuación) . Sin embargo,
- En el primer caso, desde que vuelves a lanzar con
throw;
(que, a diferencia dethrow err;
conserva el objeto de excepción original, con sus modificaciones, en dicha "ubicación mágica" en0x98e7058
) reflejará la llamada a append () - En el segundo caso, dado que arrojas algo explícitamente, se creará una copia de
err
luego se lanzará de nuevo (a una "ubicación mágica" diferente0x98e70b0
- porque todo lo que el compilador sabeerr
podría ser un objeto en la pila a punto de ser desenrollado , comoe
estaba en0xbfbce430
, no en la "ubicación mágica" en0x98e7058
), por lo que perderá los datos específicos de la clase derivada durante la construcción de copia de una instancia de clase base.
Un programa simple para ilustrar lo que está pasando:
#include <stdio.h>
struct MyErr {
MyErr() {
printf(" Base default constructor, this=%p/n", this);
}
MyErr(const MyErr& other) {
printf(" Base copy-constructor, this=%p from that=%p/n", this, &other);
}
virtual ~MyErr() {
printf(" Base destructor, this=%p/n", this);
}
};
struct MyErrDerived : public MyErr {
MyErrDerived() {
printf(" Derived default constructor, this=%p/n", this);
}
MyErrDerived(const MyErrDerived& other) {
printf(" Derived copy-constructor, this=%p from that=%p/n", this, &other);
}
virtual ~MyErrDerived() {
printf(" Derived destructor, this=%p/n", this);
}
};
int main() {
try {
try {
MyErrDerived e;
throw e;
} catch (MyErr& err) {
printf("A Inner catch, &err=%p/n", &err);
throw;
}
} catch (MyErr& err) {
printf("A Outer catch, &err=%p/n", &err);
}
printf("---/n");
try {
try {
MyErrDerived e;
throw e;
} catch (MyErr& err) {
printf("B Inner catch, &err=%p/n", &err);
throw err;
}
} catch (MyErr& err) {
printf("B Outer catch, &err=%p/n", &err);
}
return 0;
}
Resultado:
Base default constructor, this=0xbfbce430
Derived default constructor, this=0xbfbce430
Base default constructor, this=0x98e7058
Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
Derived destructor, this=0xbfbce430
Base destructor, this=0xbfbce430
A Inner catch, &err=0x98e7058
A Outer catch, &err=0x98e7058
Derived destructor, this=0x98e7058
Base destructor, this=0x98e7058
---
Base default constructor, this=0xbfbce430
Derived default constructor, this=0xbfbce430
Base default constructor, this=0x98e7058
Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
Derived destructor, this=0xbfbce430
Base destructor, this=0xbfbce430
B Inner catch, &err=0x98e7058
Base copy-constructor, this=0x98e70b0 from that=0x98e7058
Derived destructor, this=0x98e7058
Base destructor, this=0x98e7058
B Outer catch, &err=0x98e70b0
Base destructor, this=0x98e70b0
Ver también:
¿Afectará lo siguiente () en el catch la excepción rethrown para ver el efecto de que se llame a append ()?
try {
mayThrowMyErr();
} catch (myErr &err) {
err.append("Add to my message here");
throw; // Does the rethrow exception reflect the call to append()?
}
De manera similar, si lo reescribo de esta manera, ¿se producirá el corte de bits si myErr obtiene la excepción real?
try {
mayThrowObjectDerivedFromMyErr();
} catch (myErr &err) {
err.append("Add to my message''s base class here");
throw err; // Do I lose the derived class exception and only get myErr?
}
Esta pregunta es bastante antigua y tiene una respuesta apropiada para el momento en que se hizo. Sin embargo, solo quiero agregar una nota sobre cómo hacer un manejo de excepciones adecuado desde C ++ 11 y creo que esto se corresponde muy bien con lo que intentabas lograr con tu función de agregar:
Use std::nested_exception
y std::throw_with_nested
Se describe en here y here , cómo puede obtener un seguimiento de sus excepciones dentro de su código sin necesidad de un depurador o un registro engorroso, simplemente escribiendo un controlador de excepciones adecuado que volverá a generar excepciones anidadas.
¡Ya que puede hacer esto con cualquier clase de excepción derivada, puede agregar mucha información a tal retroceso! También puede echar un vistazo a mi MWE en GitHub , donde una traza inversa se vería así:
Library API: Exception caught in function ''api_function''
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
Sí, el reinicio vuelve a generar el objeto de excepción original, que ha modificado por referencia. También puede tomar una referencia de clase base, modificarla y aún así volver a lanzar el tipo de excepción derivada original por throw;
.
para la primera pregunta, sí.
pero para el segundo, refiérase a la respuesta de Vlad. Deberá diseñar cuidadosamente su objeto de excepción para manejar el copiador. por convención, la clase base no reconoce a su hijo, por lo que es muy probable que pierda los datos adicionales transportados por la clase derivada.