C ++, що охоплює всі винятки


244

Чи є еквівалент c ++ Java

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

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



2
Зауважте, що більшість збоїв не спричинені винятками в C ++. Ви можете зафіксувати всі винятки, але це не запобіжить багатьом збоям.
Mooing Duck

Відповіді:


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

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

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

68
Добре застосовувати винятки за допомогою посилань на const. Як у: catch (std :: виключення const & ex) {/ * ... * /}
coryan

12
@coryan: Чому корисно ловити за допомогою посилання const?
Тім МБ

19
Уникнення зайвих копій - одна перевага.
Грег Д

21
-1: припущення про те, що це "спіймає всі винятки в C ++", вводить в оману. Спробуйте створити помилку ділення на нуль всередині блоку спробу. Ви побачите, що він генерує виняток, який не вловлюється, але код чітко міститься в C ++. Було б корисніше констатувати, що це "спричинить всі винятки C ++", а потім додасть до приміток про обмежену корисність деякі згадки про структуровані винятки.
оматай

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

151

Хтось повинен додати, що в C ++-коді неможливо зловити "збої". Вони не кидають винятків, але роблять все, що їм подобається. Коли ви бачите програму, яка виходить з ладу через скажімо затримку з нульовим вказівником, вона робить не визначене поведінку. Немаєstd::null_pointer_exception . Спроба зловити винятки не допоможе.

Просто на випадок, коли хтось читає цю тему і думає, що може отримати причину збоїв у програмі. Замість цього слід використовувати налагоджувач, як gdb.


4
Ну, як зазначає Shy, це можливо з компілятором VC. Це не дуже гарна ідея, але це можливо.
Shog9

7
так, з SEH. але не з нормальними стандартними методами c ++ :) Добре, якщо ви будете дотримуватися вікон, ви можете майже все зробити :)
Йоханнес Шауб - ліб

1
Ммм ... спасибі за цей примх. Я шукав відповідь на те, чому мої винятки з нульовим покажчиком не трапляються!
Dalin Seivewright

10
Ви можете зафіксувати segfault за допомогою SEH у Windows та сигнал (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 (...)
{
    std::clog << boost::current_exception_diagnostic_information() << std::endl;
}

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

Зауважимо, що ...всерединіcatch є справжній еліпсис, тобто. три крапки.

Однак, оскільки винятки C ++ не обов'язково є підкласами базового Exceptionкласу, не існує жодного способу побачити змінну винятків, яка викидається при використанні цієї конструкції.


24
У C ++ 11 є: спробуйте {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(), що поверне a 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 Як це обробляє випадки, коли ви керували, щоб створити нове коло, але це не вдалося на смузі. Або коли конструктор бар намагається відкрити файл, але не вдається і тому кидає. тоді ви, можливо, опинитесь з розгубленим фолом
Меллестер

2
@MelleSterk Чи не буде стек все-таки очищатися в такому випадку, який би запустив Fooдеструктор? Я подумав, що це вся суть RAII. Однак, якщо вам потрібен вказівник, Fooа не просто створення символу Fooна стеці, вам потрібно буде загортати покажчик у щось інше, що оголошено в стеку.
reirab

так, автоматично foo = std :: make_unique <Foo> (); auto bar = std :: make_unique <Bar> (); // виняток безпечний і не протікає, не потрібен улов (...)
паульм

Ця відповідь заслуговує на голосування, якщо тільки за обговорення, яке вона розпочалася :)
Крістік

21

це можна зробити, написавши:

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

Але тут є дуже непомітний ризик: ви не можете знайти точний тип помилки, який був кинутий у tryблок, тому використовуйте цей тип, catchколи ви впевнені, що незалежно від типу винятку, програма повинна зберігатися способом, визначеним у catchблоці.


31
Я сподіваюся, що ви отримали якийсь знак для відповіді на питання майже через 5 років після того, як була надана вища відповідь!


18

Можна використовувати

catch(...)

але це дуже небезпечно. У своїй книзі « Налагодження Windows» Джон Роббінс розповідає військову історію про справді неприємну помилку, яку замаскували командою catch (...). Вам набагато краще ловити конкретні винятки. Ловіть все, що ви думаєте, ваш спробувальний блок може розумно кинути, але нехай код кидає виняток вище, якщо трапиться щось дійсно несподіване.


1
Я просто впіймав деякі звичаї з них і переглядав деякі записи на цьому етапі. Якщо нічого не робити, за винятком, це, безумовно, вимагає неприємностей.
jxramos

15

Дозвольте тут згадати лише таке: Java

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

НЕ МОЖЕ вловлювати всі винятки! У мене насправді раніше таке траплялося, і це викликає ненаситність; Виняток походить від Throwable. Так буквально, щоб зловити все, ви НЕ хочете зловити Винятки; ви хочете зловити Throwable.

Я знаю, що це звучить нерозумно, але коли ви витратили кілька днів, намагаючись з'ясувати, звідки взявся "неприхований виняток" у коді, який був оточений блоком "спробувати ... ти.


2
Звичайно, ви ніколи не повинні ловити об'єкти помилок - якщо ви повинні були їх ловити, вони були б винятками. Об'єкти помилок - це абсолютно фатальні речі, наприклад, не вистачає місця у купі тощо.
SCdF

1
Ні винятки з виконання часу, які є більшістю разів GoodProgrammerОчікувані винятки !!!
OscarRyz

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

1
Звичайно, catch(Exception)можливо, не вичерпуються всі винятки в Java, ви змішуєте це з C # ... Java = catch(Thowable), C # = catch(Exception). Не плутайте їх.
Шеф-фараон

2
@OscarRyz Це звучить як CoderMalfunctionError(що насправді є справжнім Errorпідкласом Java ... хоча це не означає, що це звучить.)
reirab

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
Я, швидше за все, підозрюю, що ви думаєте про випадки, коли ви можете переслідувати деякий загальний порядок помилок, зручно ігноруючи ті, де стек потрапив у коробку, або пам'ять вичерпана, і загальне поводження з помилками теж не вдасться. Нічого поганого в помилках уловлювання, з яких ви можете відновити, але IMHO все-таки має існувати лише як ізольований (окремий стек, заздалегідь виділена пам'ять), ретельно написана логіка, закликана перед завершенням програми; якщо ви не знаєте, в чому проблема, ви не можете бути впевнені, що з неї можна вилікуватися.
Shog9

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

3
  1. Чи можете ви запустити додаток Java, що використовує JNI, у вікні консолі (запустіть його з командного рядка java), щоб побачити, чи є звіт про те, що можливо було виявлено до того, як JVM був розбитий. Під час запуску безпосередньо як віконної програми Java, у вас можуть бути відсутні повідомлення, які з’являться, якщо ви замість цього запустите з вікна консолі.

  2. По-друге, чи можете ви заглушити реалізацію DLL DLI, щоб показати, що методи у вашу 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 ++ говорить, що улов (...) повинен виловлювати будь-які винятки, але це не у всіх випадках.

Принаймні з того, що я спробував.


1

Бережись

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

фіксує лише винятки на мовному рівні, інші винятки / помилки низького рівня, такі як Access Violationі Segmentation Faultне можуть бути схоплені.


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