Ви (дійсно) пишете безпечний код для виключення? [зачинено]


312

Здається, що обробка винятків (EH) є сучасним стандартом, і, здійснюючи пошук в Інтернеті, я не можу знайти жодних нових ідей чи методів, які намагаються вдосконалити або замінити (ну, деякі варіанти існують, але нічого нового).

Хоча більшість людей здається, що це ігнорують або просто приймають, EH має величезні недоліки: винятки для коду невидимі, і це створює багато, багато можливих точок виходу. Джоел про програмне забезпечення написав статтю про це . Порівняння, що gotoвідповідає ідеальному, змусило мене ще раз замислитися про EH.

Я намагаюся уникати EH і просто використовувати значення повернення, зворотні дзвінки або все, що відповідає цілі. Але коли вам доводиться писати надійний код, ви просто не можете ігнорувати EH в ці дні : він починається з newвибору, який може кинути виняток, а не просто повертати 0 (як колись). Це робить будь-який рядок коду С ++ вразливим до винятку. А потім більше місць у фундаментальному коді C ++ викидають винятки ... std lib це робить тощо.

Це відчуває, як ходити по хитких місцях .. Отже, тепер ми змушені дбати про винятки!

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

EH замінив старий чистий детермінований підхід (повернення значень ..), який мав лише декілька, але зрозумілих і легко вирішуваних недоліків підходом, який створює у вашому коді багато можливих точок виходу, і якщо ви почнете писати код, який фіксує винятки (що ви змушені це робити в якийсь момент), тоді він навіть створює безліч шляхів через ваш код (код у блоках лову, подумайте про серверну програму, де вам потрібні засоби реєстрації, крім std :: cerr ..). EH має переваги, але це не в цьому.

Мої актуальні запитання:

  • Ви справді пишете безпечний код для виключення?
  • Ви впевнені, що ваш останній код "готовий до виробництва" є безпечним для виключень?
  • Ви навіть можете бути впевнені, що це так?
  • Чи знаєте ви та / або насправді використовуєте альтернативи, які працюють?

44
"EH замінив старий чистий детермінований підхід (повернення значень ..)" Що? Винятки такі ж детерміновані, як і коди повернення. У них немає нічого випадкового.
rlbond

2
EH є стандартом вже давно (з часу Ada навіть!)

12
Чи є "EH" встановленою абревіатурою для обробки винятків? Я ніколи цього не бачив.
Томас Падрон-Маккарті

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

4
@frunsi: Хе, вибачте за те, що вас плутали! Але я думаю, що, якщо що, дивна і незрозуміла абревіатура або абревіатура більше відлякує людей.
Томас Падрон-Маккарті

Відповіді:


520

У вашому запитанні стверджується, що "Писати безпечний виняток код дуже важко". Я відповім спочатку на ваші запитання, а потім - на приховане запитання, що стоїть за ними.

Відповідаючи на запитання

Ви справді пишете безпечний код для виключення?

Звичайно, я.

Це причина Java втратив багато своєї привабливості для мене як програміста (відсутність RAII семантики) C ++, але я відволікся: Це C ++ питання.

Насправді це потрібно, коли вам потрібно працювати з STL або Boost кодом. Наприклад, потоки C ++ ( boost::threadабо std::thread) викинуть виняток, щоб вийти вишукано.

Ви впевнені, що ваш останній код "готовий до виробництва" є безпечним для виключень?

Ви навіть можете бути впевнені, що це так?

Написання безпечного для винятку коду схоже на написання коду без помилок.

Ви не можете бути на 100% впевнені, що ваш код є безпечним для виключень. Але тоді ви прагнете до цього, використовуючи відомі візерунки та уникаючи відомих анти-шаблонів.

Чи знаєте ви та / або насправді використовуєте альтернативи, які працюють?

У C ++ немає життєздатних альтернатив (тобто вам потрібно буде повернутися назад до C та уникати бібліотек C ++, а також зовнішніх сюрпризів, таких як Windows SEH).

Написання безпечного коду виключення

Щоб написати безпечний код виключення, ви повинні спочатку знати, який рівень безпеки винятків має кожна інструкція, яку ви пишете.

Наприклад, викид newможе кинути виняток, але призначення вбудованого (наприклад, int чи покажчика) не вийде з ладу. Свід ніколи не вийде з ладу (ніколи не напишіть заміну кидок), std::list::push_backможе кинути ...

Гарантія винятку

Перше, що потрібно зрозуміти, це те, що ви повинні мати можливість оцінити гарантію виключення, яку пропонують усі ваші функції:

  1. жоден : Ваш код ніколи цього не повинен пропонувати. Цей код просочить усе і зламається при першому кинутому винятку.
  2. основні : Це гарантія, яку ви повинні принаймні запропонувати, тобто, якщо виняток буде викинуто, ресурси не просочуються, а всі об'єкти все ще цілі
  3. сильний : обробка буде або успішною, або викине виняток, але якщо вона кидає, то дані будуть у тому самому стані, як якщо б обробка не почалася взагалі (це дає транзакційну потужність на C ++)
  4. nothrow / nofail : обробка буде успішною.

Приклад коду

Наступний код здається правильним C ++, але по правді, він пропонує гарантію "жодного", і, таким чином, він невірний:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   X * x = new X() ;                // 2. basic : can throw with new and X constructor
   t.list.push_back(x) ;            // 3. strong : can throw
   x->doSomethingThatCanThrow() ;   // 4. basic : can throw
}

Я пишу на увазі весь свій код з таким аналізом.

Найнижча пропонована гарантія є базовою, але тоді, впорядкування кожної інструкції робить всю функцію "жодної", тому що якщо 3. кидає, x просочиться.

Перше, що потрібно зробити, було б зробити функцію "базовою", тобто поставити х у розумний вказівник, поки не буде надійно володіти списком:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   std::auto_ptr<X> x(new X()) ;    // 2.  basic : can throw with new and X constructor
   X * px = x.get() ;               // 2'. nothrow/nofail
   t.list.push_back(px) ;           // 3.  strong : can throw
   x.release() ;                    // 3'. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 4.  basic : can throw
}

Тепер наш код пропонує "основну" гарантію. Ніщо не просочиться, і всі об’єкти будуть у правильному стані. Але ми могли б запропонувати більше, тобто сильну гарантію. Ось це може дорого коштувати, і саме тому не всі C ++-коди є сильними. Давайте спробуємо:

void doSomething(T & t)
{
   // we create "x"
   std::auto_ptr<X> x(new X()) ;    // 1. basic : can throw with new and X constructor
   X * px = x.get() ;               // 2. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 3. basic : can throw

   // we copy the original container to avoid changing it
   T t2(t) ;                        // 4. strong : can throw with T copy-constructor

   // we put "x" in the copied container
   t2.list.push_back(px) ;          // 5. strong : can throw
   x.release() ;                    // 6. nothrow/nofail
   if(std::numeric_limits<int>::max() > t2.integer)  // 7.   nothrow/nofail
      t2.integer += 1 ;                              // 7'.  nothrow/nofail

   // we swap both containers
   t.swap(t2) ;                     // 8. nothrow/nofail
}

Ми переупорядкували операції, спочатку створивши і встановивши Xправильне значення. Якщо будь-яка операція не вдається, вона tне змінюється, тож операція 1-3 може вважатися "сильною": якщо щось кидає, tне змінюється і Xне протікає, оскільки йому належить смарт-покажчик.

Потім ми створюємо копію t2з t, і працювати на цій копії від операції 4 до 7. Якщо що - то кидає, t2модифікується, але потім, по- t, як і раніше є оригінальним. Ми все ще надаємо сильну гарантію.

Потім обміняємо tі t2. Операції swap повинні бути нерозробленими на C ++, тому сподіваємось, що своп, про який ви писали, не Tє норовим (якщо його немає, перепишіть його, щоб він не був).

Отже, якщо ми дійшли до кінця функції, все вдалося (Не потрібно повертати тип) і tмає своє виняткове значення. Якщо вона не вдається, то tвсе ще має своє початкове значення.

Тепер пропонувати гарантію може бути досить дорого, тому не прагніть надавати гарантію на весь ваш код, але якщо ви можете це зробити без витрат (а вбудовування C ++ та інша оптимізація можуть зробити весь код вище безцінним) , то зробіть це. Користувач функції вдячний вам за це.

Висновок

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

Звичайно, компілятор C ++ не створить резервну копію гарантії (в моєму коді я пропоную гарантію як тег доксігену @warning), що начебто сумно, але це не повинно заважати вам намагатися написати код, захищений від винятків.

Нормальний збій проти помилки

Як програміст може гарантувати, що функція без збоїв завжди буде успішною? Зрештою, у функції може бути помилка.

Це правда. Гарантії винятку повинні надаватися кодом без помилок. Але тоді, будь-якою мовою, виклик функції передбачає, що функція відсутня помилок. Жоден здоровий код не захищає себе від можливості появи помилки. Напишіть код найкращим чином, а потім запропонуйте гарантію з припущенням, що це відсутність помилок. А якщо помилка є, виправте її.

Винятки становлять винятковий збій в обробці, а не помилки коду.

Останні слова

Тепер питання "чи варто цього?".

Звичайно, так і є. Функція "nothrow / no-fail", знаючи, що функція не вийде з ладу - це велика користь. Те ж саме можна сказати і про "сильну" функцію, яка дозволяє писати код із трансакційною семантикою, як, наприклад, бази даних, з функціями фіксації / відкоту, при цьому комісія є звичайним виконанням коду, а винятки становлять відкат.

Тоді "базовий" - це найменша гарантія, яку ви повинні запропонувати. C ++ - це дуже сильна мова з його областями, що дозволяє вам уникнути будь-яких витоків ресурсів (те, що сміттєзбірнику важко буде запропонувати для баз даних, з'єднань або файлів).

Отже, наскільки я розумію, це є варто.

Редагувати 2010-01-29: Про некидальний своп

nobar зробив коментар, який, на мою думку, є досить актуальним, оскільки це частина "як ви пишете безпечний код виключення":

  • [мені] своп ніколи не вийде з ладу (навіть не пишіть своп кидок)
  • [nobar] Це хороша рекомендація для swap()функцій, написаних на замовлення . Слід зазначити, однак, що це std::swap()може бути невдалим на основі операцій, які він використовує внутрішньо

за замовчуванням std::swapбуде зроблено копії та завдання, які для деяких об'єктів можуть кинутись. Таким чином, своп за замовчуванням міг би бути кинутим, або він використовується для ваших класів, або навіть для класів STL. Що стосується стандарту C ++, то операція swap для vector, dequeі listне кидає, тоді як це може бути, mapякщо функція порівняння може кинутись на конструювання копій (Див . C ++ Мова програмування, Спеціальне видання, додаток E, E.4.3 .Поміняти ).

Дивлячись на реалізацію свопу Visual C ++ 2008, його своп не кине, якщо два вектори мають однаковий розподільник (тобто звичайний випадок), але зробить копії, якщо вони мають різні розподільники. І, таким чином, я припускаю, що це може кинути цю останню справу.

Отже, оригінальний текст все ще зберігається: Ніколи не пишіть заміну кидок, але коментар nobar повинен пам’ятати: Будьте впевнені, що об’єкти, які ви міняєте, мають некидальний своп.

Редагувати 2011-11-06: Цікава стаття

Дейв Ейбрахамс , який дав нам основні / сильні / невикористані гарантії , описав у статті свій досвід щодо безпечного виключення STL:

http://www.boost.org/community/exception_safety.html

Подивіться на 7-й пункт (Автоматизоване тестування на виняток-безпека), де він покладається на автоматичне тестування одиниць, щоб переконатися, що кожен випадок перевірений. Я здогадуюсь, що ця частина є чудовою відповіддю на запитання автора " Чи можете ви навіть бути впевнені, що це так? ".

Редагувати 31.05.2013: Коментар від dionadar

t.integer += 1;без гарантії, що переповнення не відбудеться НЕ виняток безпечно, і фактично може технічно викликати UB! (Переповнений підпис під UB: C ++ 11 5/4 "Якщо під час оцінки виразу результат математично не визначений або не знаходиться в діапазоні репрезентативних значень для його типу, поведінка не визначена". цілі числа не переповнюються, але виконують свої обчислення в класі еквівалентності по модулю 2 ^ # біт.

Діонадар має на увазі наступний рядок, який справді має невизначену поведінку.

   t.integer += 1 ;                 // 1. nothrow/nofail

Рішення тут полягає в тому, щоб перевірити, чи ціле число вже на своєму максимальному значенні (використовуючи std::numeric_limits<T>::max()), перш ніж робити додавання.

Моя помилка піде в розділі "Нормальний збій проти помилки", тобто помилка. Це не скасовує міркування, і це не означає, що безпечний для винятків код марний, оскільки неможливо досягти. Ви не можете захистити себе від вимкнення комп’ютера, від помилок компілятора, навіть від ваших помилок чи інших помилок. Ви не можете досягти досконалості, але можете спробувати наблизитися якомога ближче.

Я виправив код на увазі коментаря Діонадара.


6
Дуже дякую! Я все ще сподівався на щось інше. Але я приймаю вашу відповідь за дотримання C ++ та за ваше детальне пояснення. Ви навіть спростували моє порушення "Це робить будь-який рядок коду С ++ вразливим до винятку". Цей аналіз за рядком має сенс зрештою ... я мушу подумати над цим.
Фрунсі

8
"Зміна ніколи не провалиться". Це хороша рекомендація для написаних на замовлення функцій swap (). Слід зазначити, однак, що std :: swap () може вийти з ладу на основі операцій, які він використовує внутрішньо.
nobar

7
@Mehrdad:: "finally" does exactly what you were trying to achieveЗвичайно, так і є. А оскільки легко створити крихкий код finally, поняття "пробний ресурс" нарешті було введено в Java 7 (тобто через 10 років після C # usingі 3 десятиліття після деструкторів C ++). Цю крихкість я критикую. Щодо Just because [finally] doesn't match your taste (RAII doesn't match mine, [...]) doesn't mean it's "failing": У цьому галузь не погоджується з вашим смаком, оскільки мови, зібрані зі сміттям, як правило, додають заяви, натхнені RAII (C # usingі Java try).
paercebal

5
@Mehrdad:: RAII doesn't match mine, since it needs a new struct every single darn time, which is tedious sometimesНі, це не так. Ви можете використовувати розумні покажчики або класи утиліти для "захисту" ресурсів.
paercebal

5
@Mehrdad: "це змушує вас зробити обгортку для чогось, для чого, можливо, вам не знадобиться обгортка" - при кодуванні в RAII моді вам не знадобляться будь-які обгортки: ресурс ресурсу - це об'єкт, а термін експлуатації об'єктів - ресурс. Однак, я родом із світу C ++, я зараз борюся з проектом Java. Порівняно з RAII в C ++, що "ручне управління ресурсами" на Java - МЕСЕ! Моя думка, але Java давно торгувала "автоматичною mgmt пам'яті" за "автоматичний ресурс mgmt".
Фрунсі

32

Писати безпечний для винятку код у C ++ - це не стільки використання багато блоків спробу {} catch {}. Йдеться про документування того, які гарантії надає ваш код.

Я рекомендую прочитати серії " Гуру тижня " Герба Саттера , зокрема, партіями 59, 60 та 61.

Підсумовуючи, ви можете забезпечити три рівні безпеки виключень:

  • Основні: Коли ваш код кидає виняток, ваш код не просочується ресурсами, а об'єкти залишаються руйнівними.
  • Сильний: Коли ваш код видаляє виняток, він залишає стан програми незмінним.
  • Без кидок: Ваш код ніколи не викидає винятків.

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


1
Його книга "Винятковий С ++" також добре читається. Але я все ж намагаюся поставити під сумнів концепцію ЕГ ...
Фрунсі

8
+1 OP співставляє обробку винятків (виловлює їх) з винятком безпеки (зазвичай більше про RAII)
jk.

Я підозрюю, що дуже мало коду виробництва C ++ є виключно безпечним.
Raedwald

18

Деякі з нас використовують винятки вже більше 20 років. PL / у мене є, наприклад. Положення про те, що вони - нова і небезпечна технологія, мені здається сумнівним.


Будь ласка, не зрозумійте мене неправильно, я (або намагаюся) допитуватись в ЕГ. І особливо C ++ EH. І я шукаю альтернативи. Можливо, я повинен це прийняти (і я буду, якщо це єдиний шлях), але я думаю, що можуть бути кращі альтернативи. Це не те, що я думаю, що концепція нова, але так, я думаю, що це може бути небезпечніше, ніж явна робота з помилками з поверненнями кодів ...
Frunsi

1
Якщо вам це не подобається, не використовуйте його. Поставте пробні блоки навколо речей, які потрібно викликати, які можуть кинути, і винайдіть код ye-olde-error та постраждайте від проблем, які у нього є, які в деяких випадках є терпимими.
bmargulies

Добре, ідеально, я просто буду використовувати EH та коди помилок і жити з ним. Я нитвит, я мав би прийти до цього рішення самостійно! ;)
Фрунсі

17

Насамперед (як заявив Ніл), SEH - це Структурована обробка виключень Microsoft. Він схожий на, але не ідентичний обробці винятків у C ++. Насправді вам потрібно включити обробку винятків C ++, якщо ви хочете, щоб це було в Visual Studio - поведінка за замовчуванням не гарантує, що локальні об'єкти знищуються у всіх випадках! У будь-якому випадку, поводження з винятками не дуже складне, воно просто відрізняється .

Тепер для ваших актуальних питань.

Ви справді пишете безпечний код для виключення?

Так. Я прагну до безпечного коду виключення у всіх випадках. Я евангелізую за допомогою методів RAII для розширеного доступу до ресурсів (наприклад, boost::shared_ptrдля пам'яті, boost::lock_guardдля блокування). Взагалі, послідовне використання RAII та прийомів захисту обсягу полегшить написання безпечного коду. Хитрість полягає в тому, щоб дізнатися, що існує, і як це застосувати.

Ви впевнені, що ваш останній код "готовий до виробництва" є безпечним для виключень?

Ні. Це так само безпечно, як і є. Я можу сказати, що я не бачив вини процесів через виняток протягом декількох років роботи 24/7. Я не чекаю ідеального коду, просто добре написаного коду. На додаток до забезпечення безпеки виключень, вищезазначені методи гарантують коректність способом, який майже неможливо досягти за допомогою try/ catchблоків. Якщо ви переймаєте все, що знаходиться у вашому найвищому обсязі управління (нитка, процес тощо), то ви можете бути впевнені, що ви продовжуватимете працювати за винятком винятків ( більшість часу ). Ці самі методи також допоможуть вам продовжувати правильно працювати за винятками без try/ catchскрізь .

Ви навіть можете бути впевнені, що це так?

Так. Ви можете бути впевнені, ретельний аудит коду, але ніхто це не робить? Регулярні огляди коду та ретельні розробники пройдуть довгий шлях.

Чи знаєте ви та / або насправді використовуєте альтернативи, які працюють?

Я намагався протягом декількох варіантів впродовж багатьох років, таких як кодування станів у верхніх бітах (ala HRESULTs ) або жахливий setjmp() ... longjmp()хак. Обидва вони руйнуються на практиці, хоча абсолютно по-різному.


Зрештою, якщо ви звикли застосовувати кілька прийомів і ретельно обмірковувати питання про те, де можна реально щось зробити у відповідь на виняток, у вас з’явиться дуже читабельний код, який є безпечним для винятків. Ви можете підсумувати це, дотримуючись цих правил:

  • Ви хочете бачити try/ catchколи ви можете зробити щось щодо конкретного винятку
  • Ви майже ніколи не хочете бачити сировину newчи deleteкод
  • Eschew std::sprintf, snprintfі масиви взагалі - використовувати std::ostringstreamдля форматування та заміни масивів на std::vectorіstd::string
  • Коли ви сумніваєтеся, шукайте функціональність у Boost або STL, перш ніж прокрутити свій власний

Я можу лише порекомендувати вам навчитися правильно використовувати винятки і забути про результати результатів, якщо ви плануєте писати на C ++. Якщо ви хочете уникати винятків, ви можете розглянути можливість написання іншою мовою, яка їх не має, або робить їх безпечними . Якщо ви хочете по-справжньому навчитися повноцінно використовувати C ++, прочитайте кілька книг від Herb Sutter , Nicolai Josuttis та Scott Meyers .


"поведінка за замовчуванням не гарантує, що локальні об'єкти знищуються у всіх випадках". Ви говорите, що за замовчуванням компілятор Visual Studio C ++ створює неправильний код за винятком винятків. Це справді так?
Raedwald

"Ви майже ніколи не хочете бачити сировину newчи deleteкод": під сировиною я думаю, ви маєте на увазі поза конструктором або деструктором.
Raedwald

@Raedwald - re: VC ++: видання VC2005 VC ++ не знищить локальні об'єкти, коли буде викинуто виняток SEH. Прочитайте "увімкнути обробку винятків C ++" . У VS2005 винятки SEH не викликають деструктори об'єктів C ++ за замовчуванням. Якщо ви викликаєте функції Win32 або що-небудь, визначене в DLL інтерфейсу С, вам доведеться переживати з цього приводу, оскільки вони можуть (і при нагоді) викидають виняток SEH на ваш шлях.
Д.Шоулі

@Raedwald: re: raw : в основному, deleteніколи не слід використовувати поза реалізацією tr1::shared_ptrтощо. newможе використовуватися за умови, що його використання є чимось подібним tr1::shared_ptr<X> ptr(new X(arg, arg));. Важлива частина полягає в тому, що результат newпереходу безпосередньо в керований покажчик. На сторінці " boost::shared_ptrКращі практики" описано найкраще.
Д.Шоулі

Чому в наборі правил для безпеки виключень ви посилаєтесь на std :: sprintf (та ін.)? Вони не підтверджують, що вони викидають будь-які винятки, наприклад, en.cppreference.com/w/cpp/io/c/fprintf
mabraham

10

Неможливо написати безпечний для винятку код під припущенням, що "будь-який рядок може кинути". Конструкція безпечного для винятків коду критично покладається на певні договори / гарантії, які ви повинні очікувати, дотримуватися, дотримуватися та впроваджувати у своєму коді. Це абсолютно необхідно мати код, який гарантовано ніколи не викидати. Існують і інші види гарантій виключення.

Іншими словами, створення безпечного для винятків коду значною мірою є питанням дизайну програми, а не питанням простого кодування .


8
  • Ви справді пишете безпечний код для виключення?

Ну, я, звичайно, маю намір.

  • Ви впевнені, що ваш останній код "готовий до виробництва" є безпечним для виключень?

Я впевнений, що мої сервери 24/7, побудовані за винятками, працюють 24/7 і не протікають пам'яті.

  • Ви навіть можете бути впевнені, що це так?

Дуже важко бути впевненим, що будь-який код правильний. Зазвичай можна йти лише за результатами

  • Чи знаєте ви та / або насправді використовуєте альтернативи, які працюють?

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


30
Ця відповідь не має значення.
Метт Столяр

6
@MattJoiner Тоді питання не має значення.
Майлз Рут

5

Залишаючи осторонь плутанини між винятками SEH та C ++, вам потрібно пам’ятати, що винятки можуть бути кинуті в будь-який час, і записуйте свій код, маючи на увазі. Необхідність у безпеці винятків значною мірою обумовлює використання RAII, розумних покажчиків та інших сучасних методів C ++.

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


3

EH добре, як правило. Але реалізація C ++ не є дуже доброзичливою, тому що насправді важко сказати, наскільки хороший охоплення вашого винятку. Наприклад, Java робить це легко, компілятор, як правило, не працює, якщо не обробляти можливі винятки.


2
Набагато краще з розумним використанням noexcept.
einpoklum

2

Мені дуже подобається працювати з Eclipse та Java (що нове для Java), оскільки вона видає помилки в редакторі, якщо у вас відсутній обробник EH. Це робить речі ЛОГО важче забути обробляти виняток ...

Крім того, інструменти IDE додають блок "try / catch" або інший блок "catch" автоматично.


5
Це різниця між винятками перевірених (Java) та неперевірених (c ++) (у Java теж є кілька таких). Перевага перевірених винятків - це те, що ви написали, але там у нього є свої недоліки. Google за різними підходами та різними проблемами.
Девід Родрігес - дрибес

2

Деякі з нас вважають за краще такі мови, як Java, які змушують нас оголошувати всі винятки, кинуті методами, замість того, щоб робити їх невидимими, як у C ++ та C #.

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

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

З мого досвіду, складно написати чистий код обробки винятків у C ++. Я закінчую new(nothrow)багато.


4
І ви також уникаєте більшості стандартних бібліотек? Використання new(std::nothrow)недостатньо. До речі, простіше писати безпечний для винятків код на C ++, ніж на Java: en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
avakar

3
Використання винятків, перевірених Java, сильно перебільшується. Насправді мови, що не є Java, вони НЕ вважаються успіхом. Ось чому заява "кинути" в C ++ зараз вважається застарілим, і C # ніколи не серйозно розглядав їх реалізацію (це був вибір дизайну). Для Java може бути таким
яскравим

2
З мого досвіду, писати безпечний для винятків код на C ++ не так складно, і він, як правило, призводить до більш чистого коду в цілому. Звичайно, ви повинні навчитися це робити.
Девід Торнлі

2

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

Це означає, що я дбаю про те, які лінії можна кинути. Не кожен може, і це важливо мати на увазі. Ключ насправді продумати, і створити свій код таким чином, щоб гарантувати виключення гарантій, визначених у стандарті.

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


2
  • Ви справді пишете безпечний код для виключення? [Немає такого. Винятки становлять паперовий щит до помилок, якщо у вас немає керованого середовища. Це стосується перших трьох питань.]

  • Чи знаєте ви та / або насправді використовуєте альтернативи, які працюють? [Альтернатива чому? Проблема тут полягає в тому, що люди не відокремлюють фактичні помилки від звичайної роботи програми. Якщо це нормальна робота програми (тобто файл не знайдений), це не зовсім обробка помилок. Якщо це фактична помилка, немає ніякого способу «впоратися» з нею або не є фактичною помилкою. Ваша мета тут - з’ясувати, що пішло не так, або або зупинити електронну таблицю та ввести помилку, перезапустити драйвер до тостеру, або просто попросити, щоб реактивний винищувач міг продовжувати літати навіть тоді, коли це програмне забезпечення баггі та сподіваюся на краще.]


0

Дуже багато (я навіть сказав би більшість) людей.

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

Вам потрібно активно робити помилки в обробниках, щоб отримати щось небезпечне, і лише catch (...) {} буде порівняно з ігноруванням коду помилки.


4
Неправда. Дуже легко написати код, який не є безпечним для виключень. Наприклад: f = new foo(); f->doSomething(); delete f;Якщо метод doSomething кидає виняток, то у вас є витік пам'яті.
Крістофер Джонсон

1
Не має значення, коли програма припиняється, правда? Щоб продовжити виконання, вам доведеться активно ковтати винятки. Ну, є деякі конкретні випадки, коли припинення без очищення все ще неприйнятне, але такі ситуації потребують особливої ​​обережності в будь-якій мові програмування та стилі.
има

Ви просто не можете ігнорувати винятки (і не записувати код обробки), ні в C ++, ні в керованому коді. Це буде небезпечно, і воно не буде добре себе вести. За винятком, можливо, коду іграшки.
Фрунсі

1
Якщо ви ігноруєте винятки в коді програми, програма все ще НЕ може вести себе добре, коли задіяні зовнішні ресурси. Правда, ОС піклується про закриття файлових ручок, замків, розеток тощо. Але не все обробляється, наприклад, це може залишати непотрібні файли або пошкоджувати файли під час запису до них тощо. Якщо ви ігноруєте винятки, у вас є проблема в Java, в C ++ ви можете працювати з RAII (але коли ви використовуєте техніку RAII, ви, швидше за все, використовуєте їх, тому що ви дбаєте про винятки) ...
Frunsi

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