C ++ ловит все исключения


244

Есть ли с ++ эквивалент Java

try {
    ...
}
catch (Throwable t) {
    ...
}

Я пытаюсь отладить код Java / jni, который вызывает собственные функции Windows, и виртуальная машина продолжает падать. Нативный код прекрасно работает в модульном тестировании и, похоже, вылетает при вызове через jni. Общий механизм перехвата исключений оказался бы чрезвычайно полезным.



2
Обратите внимание, что большинство сбоев не вызвано исключениями в C ++. Вы можете перехватить все исключения, но это не предотвратит многие сбои.
Mooing Duck

Ответы:


335
try{
    // ...
} catch (...) {
    // ...
}

поймает все исключения C ++, но это следует считать плохим дизайном. Вы можете использовать новый механизм current_exception в c ++ 11, но если у вас нет возможности использовать c ++ 11 (устаревшие системы кода, требующие перезаписи), то у вас нет указателя именованного исключения, который можно использовать для получения сообщения или имени , Возможно, вы захотите добавить отдельные предложения catch для различных исключений, которые вы можете поймать, и поймать только все внизу, чтобы записать неожиданное исключение. Например:

try{
    // ...
} catch (const std::exception& ex) {
    // ...
} catch (const std::string& ex) {
    // ...
} catch (...) {
    // ...
}

68
Рекомендуется ловить исключения по константной ссылке. Как в: catch (std :: exception const & ex) {/ * ... * /}
Coryan

12
@coryan: Почему стоит ловить по константной ссылке?
Тим МБ

19
Избегание ненужных копий - одно из преимуществ.
Грег Д

21
-1: предположение, что это «поймает все исключения в C ++», вводит в заблуждение. Попробуйте сгенерировать ошибку деления на ноль внутри блока try. Вы увидите, что он сгенерирует исключение, которое не будет перехвачено, но код явно написан на C ++. Было бы более полезно заявить, что это «перехватит все исключения C ++», а затем добавить некоторые упоминания о структурированных исключениях в заметки об ограниченной полезности.
omatai

42
@omatai: исправлено, он будет перехватывать все исключения C ++. Деление на ноль является неопределенным поведением и не генерирует исключение C ++.
Mooing Duck

151

Кто-то должен добавить, что никто не может поймать «сбои» в коде C ++. Те не бросают исключения, но делают все, что им нравится. Когда вы видите сбой программы из-за разыменования нулевого указателя, он выполняет неопределенное поведение. Существует нет std::null_pointer_exception. Попытка поймать исключения не поможет там.

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


4
Ну, как указывает Шай, это возможно с помощью компилятора VC. Это не очень хорошая идея, но это возможно.
Shog9

7
да с SEH. но не с нормальными стандартными методами с ++ :) хорошо, если вы придерживаетесь окон, вы можете сделать почти все :)
Йоханнес Шауб - litb

1
Ммм ... спасибо за этот лакомый кусочек. Я искал ответ относительно того, почему мои исключения нулевого указателя не были пойманы!
Далин Сейврайт

10
Вы можете перехватить segfaults с помощью SEH в Windows и signal (2) / sigaction (2) в системах POSIX, которые охватывают подавляющее большинство систем, используемых сегодня, но, как и обработка исключений, это не то, что следует использовать для нормального управления потоком. Это скорее «сделай что-нибудь полезное перед смертью».
Адам Розенфилд

1
@AdamRosenfield, пока вы не внедрили try { .. } catch(...) { ... }для отлова сигнал / сигмацию, я бы не назвал это «отлавливанием» :) Если в обработчике сигнала программисту относительно сложно узнать, где в коде произошел сбой (я говорю о программном обнаружении этого), по сравнению с try / catch.
Йоханнес Шауб - лит

72

Вот как вы можете перепроектировать тип исключения изнутри, catch(...)если вам нужно (может быть полезно при поиске неизвестного из сторонней библиотеки) с помощью GCC:

#include <iostream>

#include <exception>
#include <typeinfo>
#include <stdexcept>

int main()
{
    try {
        throw ...; // throw something
    }
    catch(...)
    {
        std::exception_ptr p = std::current_exception();
        std::clog <<(p ? p.__cxa_exception_type()->name() : "null") << std::endl;
    }
    return 1;
}

и если вы можете позволить себе использовать Boost, вы можете сделать секцию catch еще проще (снаружи) и потенциально кроссплатформенной

catch (...)
{
    std::clog << boost::current_exception_diagnostic_information() << std::endl;
}

58
try {
   // ...
} catch (...) {
   // ...
}

Обратите внимание, что ...внутри catchэто настоящий эллипсис, т.е. три точки

Однако, поскольку исключения C ++ не обязательно являются подклассами базового Exceptionкласса, нет никакого способа увидеть переменную исключения, которая выдается при использовании этой конструкции.


24
В C ++ 11 есть: try {std :: string (). At (1); // это генерирует std :: out_of_range} catch (...) {eptr = std :: current_exception (); // захват}
Мохаммад Алагган

2
@bfontaine: Да, но я сказал это, чтобы отличить catchспецификатор от существующего заполнителя кода в комментарии ( // ...), который явно не является синтаксисом C ++.
Грег Хьюгилл

1
@GregHewgill: да, это был просто типографский придирки.
bfontaine

1
@bfontaine: достаточно справедливо. :)
Грег Хьюгилл

44

невозможно (в C ++) перехватить все исключения в переносимой форме. Это потому, что некоторые исключения не являются исключениями в контексте C ++. Это включает в себя такие вещи, как деление на ноль ошибок и другие. Можно взломать и, таким образом, получить возможность генерировать исключения, когда такие ошибки случаются, но это нелегко сделать и, конечно же, нелегко получить правильную переносимость.

Если вы хотите перехватить все исключения STL, вы можете сделать

try { ... } catch( const std::exception &e) { ... }

Который позволит вам использовать e.what(), который будет возвращать const char*, который может рассказать вам больше о самом исключении. Это та конструкция, которая больше всего похожа на конструкцию Java.

Это не поможет вам, если кто-то настолько глуп, чтобы выдать исключение, которое не наследуется std::exception.


2
почему это не в топе?
Иван Санц-Караса

31

Короче, пользуйся catch(...). Тем не менее, обратите внимание, что catch(...)предполагается использовать вместе с в throw;основном:

try{
    foo = new Foo;
    bar = new Bar;
}
catch(...)       // will catch all possible errors thrown. 
{ 
    delete foo;
    delete bar;
    throw;       // throw the same error again to be handled somewhere else
}

Это правильный способ использования catch(...).


6
Лучше использовать RAII для управления памятью, которая автоматически обрабатывает такие исключительные ситуации.
Paykoob

1
@paykoob Как это обрабатывает случаи, когда вам удавалось создать новый foo, но он не удался на панели. Или когда конструктор bar пытается открыть файл, но терпит неудачу и поэтому бросает. тогда у вас может получиться фуфло
Меллестер

2
@MelleSterk Разве в этом случае стек не будет очищен, что запустит Fooдеструктор? Я думал, что в этом весь смысл RAII. Однако, если вам нужен указатель, Fooа не просто создание в Fooстеке, вам нужно будет обернуть указатель во что-то еще, что объявлено в стеке.
Рейраб

да auto foo = std :: make_unique <Foo> (); auto bar = std :: make_unique <Bar> (); // Исключительно безопасен и не будет течь, поймать не нужно (...)
Пол

Этот ответ заслуживает голосования, если только за обсуждение, которое началось :)
Cristik

21

это можно сделать, написав:

try
{
  //.......
}
catch(...) // <<- catch all
{
  //.......
}

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


31
Я надеюсь, что вы получили какой-то значок для ответа на вопрос почти через 5 лет после того, как был дан превосходный ответ!


18

Ты можешь использовать

catch(...)

но это очень опасно. В своей книге « Отладка Windows» Джон Роббинс рассказывает историю о действительно неприятной ошибке, которая была замаскирована командой catch (...). Вам гораздо лучше ловить конкретные исключения. Поймайте все, что, по вашему мнению, ваш блок try может разумно сгенерировать, но позвольте коду генерировать исключение выше, если происходит что-то действительно неожиданное.


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

15

Позвольте мне упомянуть это здесь: Java

try 
{
...
}
catch (Exception e)
{
...
}

не может поймать все исключения! У меня на самом деле были подобные вещи раньше, и это провоцирует безумство; Исключение происходит от Throwable. В буквальном смысле, чтобы поймать все, вы не хотите ловить исключения; Вы хотите поймать Throwable.

Я знаю, это звучит странно, но когда вы потратили несколько дней, пытаясь выяснить, откуда появилось «необработанное исключение» в коде, который был окружен блоком try ... catch (Exception e) », он придерживается ты.


2
Конечно, вы никогда не должны ловить объекты Error - если бы вы должны были ловить их, они были бы исключениями. Объекты ошибок - это абсолютно фатальные вещи, такие как нехватка кучи и т. Д.
SCdF

1
Нет исключений времени выполнения, которые в большинстве случаев являются исключениями GoodProgrammerExpected !!!
ОскарРиз

3
У нас была действительно серьезная ошибка, вызванная перехватом OutOfMemoryError из-за блока catch (Throwable) вместо того, чтобы позволить ему убивать вещи ...
Trejkaz

1
Конечно, catch(Exception)может не перехватить все исключения в Java, вы путаете его с C # ... Java = catch(Thowable), C # = catch(Exception). Не путай их.
шеф-повар фараон

2
@OscarRyz Это звучит как CoderMalfunctionError(который на самом деле является настоящим Errorподклассом Java ... хотя это не значит, как это звучит.)
Рейраб

9

Хорошо, если вы хотите перехватить все исключения, например, для создания минидампа ...

Кто-то сделал работу на Windows.

См. Http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus. В этой статье он объясняет, как он узнал, как перехватывать все виды исключений, и предоставляет код, который работает.

Вот список, который вы можете поймать:

 SEH exception
 terminate
 unexpected
 pure virtual method call
 invalid parameter
 new operator fault 
 SIGABR
 SIGFPE
 SIGILL
 SIGINT
 SIGSEGV
 SIGTERM
 Raised exception
C++ typed exception

И использование: CCrashHandler ch; ch.SetProcessExceptionHandlers (); // сделать это для одного потока ch.SetThreadExceptionHandlers (); // за каждый тред


По умолчанию это создает мини-дамп в текущем каталоге (crashdump.dmp)


4

Общий механизм перехвата исключений оказался бы чрезвычайно полезным.

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

То, что вы действительно хотите, это отладчик ...


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

3
Я скорее подозреваю, что вы думаете о случаях, когда вы можете следовать некоторому общему порядку действий с ошибками, удобно игнорируя те, где стек перегружен или память исчерпана, и общая обработка ошибок также не будет успешной. Нет ничего плохого в перехвате ошибок, из которых вы можете восстановиться, но ИМХО универсальное средство должно действительно существовать только в виде изолированного (отдельный стек, предварительно выделенная память), тщательно написанной логики, вызываемой непосредственно перед завершением программы; Если вы не знаете, в чем проблема, вы не можете быть уверены, что ее можно устранить.
Shog9

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

3
  1. Можете ли вы запустить Java-приложение, использующее JNI, из окна консоли (запустить его из командной строки java), чтобы увидеть, есть ли какой-либо отчет о том, что могло быть обнаружено до сбоя JVM. При запуске непосредственно как оконное приложение Java вы можете пропустить сообщения, которые появятся, если вы запустите их из окна консоли.

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

  3. На случай, если проблема заключается в неправильном использовании одного из методов интерфейса JNI из кода C ++, вы убедились, что некоторые простые примеры JNI компилируются и работают с вашей установкой? Я имею в виду, в частности, использование методов интерфейса JNI для преобразования параметров в собственные форматы C ++ и преобразования результатов функций в типы Java. Полезно заглушить их, чтобы убедиться, что преобразования данных работают, и вы не собираетесь потерпеть неудачу при COM-подобных вызовах в интерфейс JNI.

  4. Есть и другие вещи, которые нужно проверить, но трудно что-либо предложить, не зная больше о том, каковы ваши нативные методы Java и что пытается сделать их реализация JNI. Не ясно, что перехват исключения из уровня кода C ++ связан с вашей проблемой. (Вы можете использовать интерфейс JNI, чтобы перебросить исключение как Java, но из того, что вы предоставляете, не ясно, что это поможет.)


2

Для реальной проблемы невозможности правильно отладить программу, которая использует JNI (или ошибка не появляется при запуске его под отладчиком):

В этом случае часто помогает добавить обертки Java вокруг ваших вызовов JNI (т. Е. Все нативные методы являются частными, а ваши открытые методы в классе вызывают их), которые выполняют некоторую базовую проверку работоспособности (проверяют, что все «объекты» освобождены и «объекты»). не используются после освобождения) или синхронизации (просто синхронизируйте все методы из одной DLL в один экземпляр объекта). Пусть методы java-оболочки регистрируют ошибку и генерируют исключение.

Это часто помогает найти реальную ошибку (которая на удивление в основном в коде Java, который не подчиняется семантике вызываемых функций, вызывающих некоторые неприятные двойные освобождения или аналогичные) легче, чем попытка отладки массивно параллельной программы Java в родной отладчик ...

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


1

Ну, это действительно зависит от среды компилятора. GCC не ловит эти. Visual Studio и последний Borland, который я использовал, сделали.

Таким образом, вывод о сбоях заключается в том, что это зависит от качества среды разработки.

Спецификация C ++ говорит, что catch (...) должен перехватывать любые исключения, но это не во всех случаях.

По крайней мере, из того, что я пытался.


1

Знать

try{
// ...
} catch (...) {
// ...
}

ловит только языковые исключения, другие низкоуровневые исключения / ошибки, такие как Access Violationи не Segmentation Faultбудут пойманы.


Такие вещи, как ошибка сегментации, на самом деле не являются исключениями, они являются сигналами; таким образом, вы не можете поймать их как типичные исключения. Однако есть некоторые обходные пути, подобные этому .
MAChitgarha
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.