Как работает эта реализация на цепочке исключения?
-
01-10-2019 - |
Вопрос
Я ранее спросил вопрос О том, как цепить исключения в C ++, и один из ответов предоставил нефтевое решение того, как это можно сделать. Проблема в том, что я не понимаю код, и пытаюсь иметь такое обсуждение в комментариях, слишком много беспокойства. Поэтому я подумал, что лучше начать новый вопрос целиком.
Код включен ниже, и я четко пометил каждый раздел, который я не получаю. Описание того, что я не понимаю, включен ниже кода. Код был написан Потаtoswatter..
Код
struct exception_data { // abstract base class; may contain anything
virtual ~exception_data() {}
};
struct chained_exception : std::exception {
chained_exception( std::string const &s, exception_data *d = NULL )
: data(d), descr(s) {
try {
link = new chained_exception;
// ----------------------------------------------------------------
// How does this work (section 1)?
throw;
// ----------------------------------------------------------------
} catch ( chained_exception &prev ) {
// ----------------------------------------------------------------
// How does this work (section 2)?
swap( *link, prev );
// ----------------------------------------------------------------
} // catch std::bad_alloc somehow...
}
friend void swap( chained_exception &lhs, chained_exception &rhs ) {
std::swap( lhs.link, rhs.link );
std::swap( lhs.data, rhs.data );
swap( lhs.descr, rhs.descr );
}
virtual char const *what() const throw() { return descr.c_str(); }
virtual ~chained_exception() throw() {
// --------------------------------------------------------------------
// How does this work (section 3)?
if ( link && link->link ) delete link; // do not delete terminator
// --------------------------------------------------------------------
delete data;
}
chained_exception *link; // always on heap
exception_data *data; // always on heap
std::string descr; // keeps data on heap
private:
chained_exception() : link(), data() {}
friend int main();
};
void f() {
try {
throw chained_exception( "humbug!" );
} catch ( std::exception & ) {
try {
throw chained_exception( "bah" );
} catch ( chained_exception &e ) {
chained_exception *ep = &e;
for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
// Print ep->what() to std::cerr
}
}
}
try {
throw chained_exception( "meh!" );
} catch ( chained_exception &e ) {
for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
// Print ep->what() to std::cerr
}
}
}
int main() try {
// ------------------------------------------------------------------------
// How does this work (section 4)?
throw chained_exception(); // create dummy end-of-chain
// ------------------------------------------------------------------------
} catch( chained_exception & ) {
// body of main goes here
f();
}
Запуск кода дает следующий выход:
bah
humbug!
meh!
Что я не понимаю
throw;
внутриtry
-блокировать: Я никогда не видел этого раньше. Единственное место, где я подумалthrow;
быть действительным, был внутриcatch
-Бук к ритроу, что было поймано. Так что это делает? Некоторые отладки, видимо, показывают, что брошенное исключение - это то, что было брошено ранее, но это было внутри совершенно другойtry
-блокировать. На самом деле, это было даже за пределамиstruct
Декларация!Поля Swap: Почему нам нужно обмен поля исключения? Не просто копирование указателей будет достаточно? Это чтобы предотвратить это структуры, к которым полей указывает на удаление из кучи преждевременно?
Проверить
link
а такжеlink
Ссылка: Я могу понять, проверяя, чтоlink
не являетсяNULL
(Хотя даже удалениеNULL
Указатель не имеет эффекта), но почему нужно проверитьlink
ссылка?Бросить деминируемое исключение: Почему этот манекен нужен? Это брошено, но затем бросилось. Почему нам нужно это как конец цепочке?
Решение
Умный код - kudos to ptatoswatter на этом. Я думаю, что мне пришлось бы найти свой путь вокруг последнего предмета, хотя.
throw;
связаны активное исключение. Действительно только еслиcatch
блок на стеке. Я не могу вспомнить, где я наткнулся на этот тидбит, но это, вероятно, было на так в контексте какого-то другого вопроса. Голый бросок дает нам доступ к текущему исключему, поймаю его вchained_exception
конструктор. Другими словами,prev
В конструкторе является ссылкой на исключение, что мы в настоящее время обрабатываем.Вы правильно здесь. Это предотвращает двойное удаление.
Sentinel исключение, один брошенный в
main
, никогда не следует удалить. Один идентифицирующий атрибут этого исключения в том, что этоlink
член естьNULL
.Это часть, которую я не люблю, но не могу придумать легко. Единственный видимый
chained_exception
Конструктор можно назвать только когдаcatch
Блок активен. IIRC, голый бросок без активногоcatch
Блок не-нет. Итак, обходной путь - бросить вmain
и поставить весь свой код вcatch
блокировать.
Теперь, если вы попробуете этот метод в мульти-резьбовом коде, убедитесь, что вы очень хорошо понимаете (4). Вам придется повторить это в вашей точке записи по ниве.