Отлов базовых исключений c ++ - PullRequest
3 голосов
/ 12 февраля 2009

В моем проекте у нас есть базовое исключение. Для обработки показа диалогов об ошибках, лог и тому подобное. Я ищу способ обработки всех производных классов этого исключения, я думал, что это будет работать:

try
{
  main_loop();
}
catch (const MyExceptionBase* e)
{
  handle_error(e);
}

Поскольку каждый брошенный дочерний экземпляр может быть представлен указателем на его родителя. Но нет, когда создаются исключения, это необработанное исключение.

Почему это? С ++ только генерирует исключения как ссылки? Тем самым делая мой блок уловок бесполезным? Но тогда почему это вообще компилируется?

Единственное, о чем я могу думать, это:

try
{
  main_loop();
}
catch (const ExceptionA& e)
{
  handle_error(e);
}
catch (const ExceptionB& e)
{
  handle_error(e);
}
catch (const ExceptionC& e)
{
  handle_error(e);
}

Который кажется уродливым. Как правильно это сделать? У вас нет базового класса исключений? Или это можно решить так, как я хочу?

Ps: handle_error() просто использует функцию базового класса display_message_box() и полностью завершает работу программы.

Ответы [ 6 ]

16 голосов
/ 12 февраля 2009

Просто смешайте два подхода: используйте базовый класс и используйте ссылку.

try
{
  main_loop();
}
catch (const MyExceptionBase& e)
{
  handle_error(e);
}

Кстати, C ++ может ловить указатели, если вы их бросаете. Это не рекомендуется.

15 голосов
/ 12 февраля 2009

Лучше всего поймать базовую ссылку. Но, пожалуйста, делайте это по ссылке, а не по указателю. Пример

try
{
  main_loop();
}
catch (const MyExceptionBase& e)
{
  handle_error(e);
}

Проблема с перехватом исключения по указателю заключается в том, что оно должно быть выдано указателем. Это означает, что он будет создан с новым.

throw new ExceptionA();

Это оставляет довольно большую проблему, потому что она должна быть удалена в какой-то момент, иначе у вас утечка памяти. Кто должен нести ответственность за удаление этого исключения? Как правило, трудно понять это правильно, поэтому большинство людей ловят по ссылке.

В общем, в C ++ вы должны ...

Поймать по ссылке, бросить по значению

3 голосов
/ 12 февраля 2009

Вы можете использовать catch( const sometype* ptr ) только если вы бросаете указатели, что нежелательно при 99% обстоятельств.

Причина, по которой работает catch( const sometype& ptr ), заключается в том, что r-значения неявно преобразуются в постоянные ссылки.

1 голос
/ 12 февраля 2009

Я удивлен, что ваш оригинальный пример не работает. Следующее работает (по крайней мере, с g ++):

class Base { public: virtual ~Base () {} };
class Derived : public Base {};

int main ()
{
  try
  {
    throw new Derived ();
  }
  catch (Base const * b)
  {
    delete b;
  }
}

Я также довольно уверен, что это предназначено для работы в соответствии с пулой под 15.3 / 3:

обработчик имеет тип cv1 T * cv2, а E является типом указателя, который может быть преобразован в тип обработчика любым из

или обоими

Вы наследуете от базового типа исключения через публичное наследование? Базовый класс должен быть доступным, и это предотвратит перехват вашего исключения.

Как и во всех других ответах здесь, бросание / отлов по ссылке имеет преимущество в том, что вам не нужно беспокоиться о владении памятью и т. Д. Но если приведенный выше пример не работает - тогда я не знаю почему справочный пример будет работать.

1 голос
/ 12 февраля 2009

Это должно работать:

try {
  main_loop();
} catch (const MyExceptionBase &e) {
  handle_error(e);
}

Я предполагаю, что ExceptionA / B / C все наследуются от MyExceptionBase ... Я думаю, что это должно работать просто отлично.

PS: вы можете рассмотреть возможность наследования MyExceptionBase от std :: exception.

0 голосов
/ 12 февраля 2009

Как уже упоминалось, вы должны поймать ссылку на исключение базового класса.

Что касается того, почему он компилируется в первую очередь, в отличие от Java, нет ограничений на то, какие типы могут быть выброшены или перехвачены. Таким образом, вы можете вставить блок catch для любого типа, даже если он никогда не генерируется, и ваш компилятор с радостью скомпилирует его. Поскольку вы можете бросать по указателю, вы также можете поймать по указателю.

...