Вопросы об исключениях C ++ по повторному удалению исходного исключения
Вопрос
Приведет ли следующее append() в catch к тому, что исключение rethrown увидит эффект вызова append()?
try {
mayThrowMyErr();
} catch (myErr &err) {
err.append("Add to my message here");
throw; // Does the rethrow exception reflect the call to append()?
}
Аналогично, если я перепишу это таким образом, произойдет ли нарезка битов, если фактическое исключение получено myErr ?
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?
}
Решение
В обоих случаях, поскольку вы перехватываете по ссылке, вы фактически изменяете состояние исходного объекта исключения (который вы можете считать находящимся в волшебное место памяти, которое останется действительным во время последующего раскручивания -- 0x98e7058
в примере ниже).Однако,
- В первом случае, поскольку вы повторно бросаете с помощью
throw;
(что, в отличие отthrow err;
, сохраняет исходный объект исключения с вашими изменениями в указанном «магическом месте» по адресу0x98e7058
) воля отразить вызов add() - Во втором случае, поскольку вы что-то бросаете явно, копировать из
err
будет создан, а затем брошен заново (в другом «магическом месте»)0x98e70b0
-- потому что, насколько известно компиляторуerr
может быть объектом в стеке, который скоро будет раскручен, напримерe
был в0xbfbce430
, а не в «волшебном месте» по адресу0x98e7058
), так вы потеряете данные, относящиеся к производному классу во время копирования экземпляра базового класса.
Простая программа, иллюстрирующая происходящее:
#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;
}
Результат:
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
Также см:
Другие советы
Этот вопрос довольно старый, и на него есть ответ, соответствующий тому времени, когда он был задан.Однако я просто хочу добавить примечание о том, как правильно обрабатывать исключения, поскольку C++11 и я считаю, что это очень хорошо соответствует тому, чего вы пытались достичь с помощью своей функции добавления:
Использование std::nested_exception
и std::throw_with_nested
Это описано в StackOverflow здесь и здесь, как вы можете получите обратную ссылку на ваши исключения внутри вашего кода без необходимости в отладчике или громоздком протоколировании, просто написав соответствующий обработчик исключений, который повторно заполнит вложенные исключения.
Поскольку вы можете сделать это с любым производным классом исключений, вы можете добавить много информации к такой обратной трассировке!Вы также можете взглянуть на мой MWE на GitHub, где обратная трассировка выглядела бы примерно так:
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"
Да, повторное создание повторно создает исходный объект исключения, который вы изменили с помощью ссылки.Вы также можете перехватить ссылку на базовый класс, изменить ее и при этом иметь возможность повторно генерировать исходный производный тип исключения с помощью throw;
.
на первый вопрос, да.
но во-вторых, обратитесь к ответу Влада.вам нужно будет тщательно спроектировать объект исключения для обработки копирования.по соглашению базовый класс не распознает своего дочернего класса, поэтому вы, скорее всего, потеряете дополнительные данные, переносимые производным классом.