Я видел по крайней мере один надежный источник (класс C ++, который я взял) рекомендовал, чтобы классы исключений для конкретного приложения в C ++ наследовали от std::exception
. Я не совсем понимаю преимущества этого подхода.
В C # причины наследования от ApplicationException
очевидны: вы получаете несколько полезных методов, свойств и конструкторов, и вам просто нужно добавить или переопределить то, что вам нужно. С std::exception
этим , кажется , что все , что вы получите это what()
метод , чтобы переопределить, который вы могли бы точно так же создавать самостоятельно.
Итак, каковы преимущества, если таковые имеются, от использования std::exception
в качестве базового класса для моего класса исключений для конкретного приложения? Есть ли веские причины не наследовать std::exception
?
источник
Ответы:
Основное преимущество состоит в том , что код , использующий классы не должны знать точный тип того , что вы
throw
на него, а может просто . Изменить: как отметили Мартин и другие, вы действительно хотите унаследовать от одного из подклассов, объявленных в заголовке.catch
std::exception
std::exception
<stdexcept>
источник
std::exception
как определено в<exception>
заголовке ( доказательство ).-1
Проблема с
std::exception
заключается в том, что нет конструктора (в стандартных версиях), который принимает сообщение.В результате я предпочитаю выводить из
std::runtime_error
. Это является производным от,std::exception
но его конструкторы позволяют передавать C-String или astd::string
конструктору, который будет возвращен (как achar const*
) приwhat()
вызове.источник
Причина наследования
std::exception
- это "стандартный" базовый класс для исключений, поэтому для других людей в команде, например, естественно ожидать этого и ловить базуstd::exception
.Если вам нужно удобство, вы можете унаследовать от
std::runtime_error
этогоstd::string
конструктора.источник
std::exception
- отличная идея. Чего вам не следует делать, так это наследовать более чем от одного.Однажды я участвовал в очистке большой базы кода, куда предыдущие авторы добавляли целые числа, HRESULTS, std :: string, char *, случайные классы ... везде разные вещи; просто назовите тип, и он, вероятно, был куда-то брошен. И вообще никакого общего базового класса. Поверьте, все стало намного аккуратнее, когда мы дошли до того, что все брошенные типы имели общую базу, которую мы могли поймать и знать, что ничего не пройдет. Поэтому, пожалуйста, сделайте себе (и тем, кому придется поддерживать ваш код в будущем) одолжение и сделайте это с самого начала.
источник
Вы должны унаследовать от boost :: exception . Он предоставляет намного больше функций и понятные способы передачи дополнительных данных ... конечно, если вы не используете Boost , проигнорируйте это предложение.
источник
Да, вы должны исходить из
std::exception
.Другие ответили, что
std::exception
проблема заключается в том, что вы не можете передать ему текстовое сообщение, однако, как правило, не рекомендуется пытаться отформатировать сообщение пользователя в момент передачи. Вместо этого используйте объект исключения для передачи всей соответствующей информации на сайт перехвата, который затем может отформатировать удобное для пользователя сообщение.источник
std::exception
и передать информацию об исключении. Но кто будет отвечать за создание / форматирование сообщения об ошибке?::what()
Функция или что - то еще?Причина, по которой вы можете захотеть наследовать,
std::exception
заключается в том, что это позволяет вам генерировать исключение, которое перехватывается в соответствии с этим классом, то есть:источник
Есть одна проблема с наследованием, о которой вы должны знать, это нарезка объектов. Когда вы пишете
throw e;
throw-выражение, инициализирует временный объект, называемый объектом исключения, тип которого определяется удалением любых cv-квалификаторов верхнего уровня из статического типа операндаthrow
. Это может быть не то, что вы ожидаете. Пример проблемы вы можете найти здесь .Это не аргумент против наследования, это просто информация, которую нужно знать.
источник
throw;
это нормально, но не очевидно, что вы должны написать что-то подобное.raise()
как таковая:virtual void raise() { throw *this; }
. Просто не забудьте переопределить его в каждом производном исключении.Разница: std :: runtime_error vs std :: exception ()
Вы должны унаследовать это или нет, решать вам. Стандарт
std::exception
и его стандартные потомки предлагают одну возможную структуру иерархии исключений (разделение наlogic_error
подиерархию иruntime_error
подиерархию) и один возможный интерфейс объекта исключения. Если нравится - пользуйтесь. Если по какой-то причине вам нужно что-то другое - определите свою собственную структуру исключений.источник
Поскольку язык уже генерирует std :: exception, вам все равно нужно перехватить его, чтобы обеспечить достойный отчет об ошибках. Вы также можете использовать ту же уловку для всех собственных неожиданных исключений. Кроме того, почти любая библиотека, генерирующая исключения, будет наследовать их от std :: exception.
Другими словами, это либо
или
И второй вариант однозначно лучше.
источник
Если все ваши возможные исключения являются производными
std::exception
, ваш блок catch можетcatch(std::exception & e)
быть просто уверен в том, что захватит все.Захватив исключение, вы можете использовать этот
what
метод для получения дополнительной информации. C ++ не поддерживает утиную типизацию, поэтому другому классу сwhat
методом потребуется другой улов и другой код для его использования.источник
Производиться ли производным от любого стандартного типа исключения - это первый вопрос. Это позволяет использовать единый обработчик исключений для всех исключений стандартной библиотеки и ваших собственных, но также поощряет использование таких универсальных обработчиков. Проблема в том, что нужно ловить только те исключения, которые умеют обрабатывать. В main (), например, перехват всех std :: exceptions, вероятно, будет хорошей идеей, если строка what () будет регистрироваться как последнее средство перед выходом. Однако в других местах это вряд ли будет хорошей идеей.
После того, как вы решили, производиться от стандартного типа исключения или нет, возникает вопрос, что должно быть базовым. Если вашему приложению не нужен i18n, вы можете подумать, что форматирование сообщения на сайте вызова ничуть не хуже сохранения информации и генерации сообщения на сайте вызова. Проблема в том, что форматированное сообщение может не понадобиться. Лучше использовать ленивую схему генерации сообщений - возможно, с предварительно выделенной памятью. Затем, если сообщение понадобится, оно будет сгенерировано при доступе (и, возможно, кэшировано в объекте исключения). Таким образом, если сообщение создается при вызове, тогда в качестве базового класса требуется производное std :: exception, например std :: runtime_error. Если сообщение создается лениво, подходящей базой является std :: exception.
источник
Другая причина для исключений подкласса - это лучший аспект дизайна при работе с большими инкапсулированными системами. Вы можете повторно использовать его для таких вещей, как сообщения проверки, пользовательские запросы, фатальные ошибки контроллера и так далее. Вместо того, чтобы переписывать или повторно перехватывать всю вашу валидацию, например сообщения, вы можете просто «поймать» ее в основном исходном файле, но выбросить ошибку где угодно во всем вашем наборе классов.
например, фатальное исключение завершит программу, ошибка проверки только очистит стек, а пользовательский запрос задаст вопрос конечному пользователю.
Это также означает, что вы можете повторно использовать одни и те же классы, но на разных интерфейсах. например, приложение Windows может использовать окно сообщения, веб-сервис будет отображать HTML, а система отчетов будет регистрировать его и так далее.
источник
Хотя этот вопрос довольно старый и уже получил много ответов, я просто хочу добавить примечание о том, как правильно обрабатывать исключения в C ++ 11, поскольку я постоянно упускаю его в обсуждениях исключений:
Использование
std::nested_exception
иstd::throw_with_nested
Здесь и здесь описано в StackOverflow , как можно отслеживать свои исключения. внутри вашего кода без необходимости в отладчике или громоздком журналировании, просто написав правильный обработчик исключений, который повторно генерирует вложенные исключения.
Так как вы можете сделать это с любым производным классом исключений, вы можете добавить много информации в такую трассировку! Вы также можете взглянуть на мою MWE на GitHub , где трассировка будет выглядеть примерно так:
Вам даже не нужно создавать подкласс
std::runtime_error
, чтобы получить много информации при возникновении исключения.Единственное преимущество, которое я вижу в создании подклассов (вместо простого использования
std::runtime_error
), заключается в том, что ваш обработчик исключений может перехватить ваше собственное исключение и сделать что-то особенное. Например:источник