Должен ли я наследовать от std:: exception?

Я видел как минимум один надежный источник (класс С++, который я взял), рекомендую, чтобы классы исключений для конкретных приложений в С++ наследовались от std::exception. Я не понимаю преимуществ такого подхода.

В С# причины наследования из ApplicationException понятны: вы получаете несколько полезных методов, свойств и конструкторов и просто должны добавлять или переопределять то, что вам нужно. С std::exception кажется, что все, что вы получаете, это метод what() для переопределения, который вы могли бы просто создать самостоятельно.

Каковы преимущества, если таковые имеются, использования std::exception в качестве базового класса для моего класса исключений для конкретных приложений? Есть ли веские причины не наследовать от std::exception?

Ответ 1

Основное преимущество заключается в том, что код, использующий ваши классы, не должен знать точный тип того, что вы throw на нем, но может просто catch std::exception.

Изменить:, как отметил Мартин и другие, вы действительно хотите получить один из подклассов std::exception, объявленных в заголовке <stdexcept>.

Ответ 2

Проблема с std::exception заключается в том, что нет конструктора (в стандартно совместимых версиях), который принимает сообщение.

В результате я предпочитаю выводить из std::runtime_error. Это происходит от std::exception, но его конструкторы позволяют передать C-String или std::string в конструктор, который будет возвращен (как char const*), когда вызывается what().

Ответ 3

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

Если вы ищете удобство, вы можете наследовать от std::runtime_error, который предоставляет конструктор std::string.

Ответ 4

Я когда-то участвовал в очистке большой кодовой базы, где предыдущие авторы бросили ints, HRESULTS, std::string, char *, случайные классы... разные вещи повсюду; просто назовите тип, и его, вероятно, бросили куда-то. И вообще никакого базового класса вообще нет. Поверьте, все было намного более аккуратно, как только мы дошли до того, что у всех брошенных типов была общая база, которую мы могли поймать, и знать, что ничего не пройдет. Поэтому, пожалуйста, сделайте сами (и тем, кто должен будет поддерживать ваш код в будущем), и сделайте это с самого начала.

Ответ 5

Вы должны наследовать от boost:: exception. Он предоставляет намного больше возможностей и понятных способов переноса дополнительных данных... конечно, если вы не используете Boost, затем проигнорируйте это предложение.

Ответ 6

Причина, по которой вы захотите наследовать от std::exception, заключается в том, что она позволяет вам вызывать исключение, которое попадает в соответствии с этим классом, то есть:

class myException : public std::exception { ... };
try {
    ...
    throw myException();
}
catch (std::exception &theException) {
    ...
}

Ответ 7

Да вы должны получить от std::exception.

Другие ответили, что std::exception имеет проблему с тем, что вы не можете передать ему текстовое сообщение, однако, как правило, не рекомендуется пытаться отформатировать сообщение пользователя в точке броска. Вместо этого используйте объект исключения для переноса всей соответствующей информации на сайт catch, который затем может форматировать удобное для пользователя сообщение.

Ответ 8

Существует одна проблема с наследованием, о которой вы должны знать, это обрезка объектов. Когда вы пишете throw e;, выражение throw инициализирует временный объект, называемый объектом исключения, тип которого определяется удалением всех cv-квалификаторов верхнего уровня из статического типа операнда throw. Это может быть не то, что вы ожидаете. Пример проблемы вы можете найти здесь.

Это не аргумент против наследования, он просто "должен знать" информацию.

Ответ 9

Разница: std:: runtime_error vs std:: exception()

Следует ли вам наследовать или нет, зависит от вас. Стандарт std::exception и его стандартные потомки предлагают одну возможную структуру иерархии исключений (деление на суб-иерархию logic_error и суб-иерархию runtime_error) и один возможный интерфейс объекта исключений. Если вам это нравится - используйте его. Если по какой-то причине вам нужно что-то другое - определите свою собственную инфраструктуру исключения.

Ответ 10

Если все ваши возможные исключения производятся от std::exception, ваш блок catch может просто catch(std::exception & e) и быть уверенным в захвате всего.

Как только вы захватили исключение, вы можете использовать этот метод what для получения дополнительной информации. С++ не поддерживает утиную печать, поэтому для другого класса с методом what для его использования потребуется другой улов и другой код.

Ответ 11

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

Другими словами, его либо

catch (...) {cout << "Unknown exception"; }

или

catch (const std::exception &e) { cout << "unexpected exception " << e.what();}

И второй вариант определенно лучше.

Ответ 12

Является ли вывод из любого стандартного типа исключения или нет, это первый вопрос. Это позволяет использовать один обработчик исключений для всех стандартных библиотек и ваших собственных, но также поощряет таких обработчиков catch-them-all. Проблема в том, что нужно только ловить исключения, с которыми можно справиться. В main(), например, ловить все std:: exceptions, вероятно, хорошо, если строка what() будет регистрироваться как последнее средство перед выходом. Однако в других местах это вряд ли будет хорошей идеей.

Как только вы решите, следует ли выводить из стандартного типа исключения или нет, тогда вопрос должен быть базой. Если вашему приложению не требуется i18n, вы можете подумать, что форматирование сообщения на сайте вызова не хуже, чем сохранение информации и генерирование сообщения на сайте вызова. Проблема в том, что отформатированное сообщение может не понадобиться. Лучше использовать ленивую схему генерации сообщений - возможно, с предопределенной памятью. Затем, если сообщение необходимо, оно будет сгенерировано при доступе (и, возможно, кэшировано в объекте исключения). Таким образом, если сообщение генерируется при броске, то в качестве базового класса требуется деривация std:: exception, такая как std:: runtime_error. Если сообщение генерируется лениво, то std:: exception является подходящей базой.

Ответ 13

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

например. Фатальное исключение прекратит выполнение программы, ошибка проверки приведет к очистке стека, и пользовательский запрос задаст конечный пользователь вопрос.

Выполнение этого способа также означает, что вы можете повторно использовать одни и те же классы, но на разных интерфейсах. например Приложение Windows может использовать окно сообщения, веб-служба будет показывать html, а система отчетов будет регистрировать его и т.д.

Ответ 14

Хотя этот вопрос довольно старый и уже получил много ответов, я просто хочу добавить примечание о том, как выполнять правильную обработку исключений в С++ 11, так как я постоянно теряю это в дискуссиях об исключениях:

Используйте std::nested_exception и std::throw_with_nested

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

Поскольку вы можете сделать это с помощью любого производного класса исключения, вы можете добавить много информации в такую ​​обратную линию! Вы также можете взглянуть на мой 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"

Вам даже не требуется подкласс std::runtime_error, чтобы получить много информации при вызове исключения.

Единственное преимущество, которое я вижу в подклассе (вместо использования std::runtime_error), заключается в том, что ваш обработчик исключений может поймать ваше собственное исключение и сделать что-то особенное. Например:

try
{
  // something that may throw
}
catch( const MyException & ex )
{
  // do something specialized with the
  // additional info inside MyException
}
catch( const std::exception & ex )
{
  std::cerr << ex.what() << std::endl;
}
catch( ... )
{
  std::cerr << "unknown exception!" << std::endl;
}