Чи слід виводити / успадковувати з std :: виключення?


15

Розробляючи свою першу "серйозну" бібліотеку C ++, я запитую себе:

Це гарний стиль отримувати винятки з std::exceptionпотомства ?!

Навіть після читання

Я досі не впевнений. Тому що, крім звичайної (але може не непоганої) практики, я б припустив, що як користувач бібліотеки, що функція бібліотеки кидає std::exceptions лише тоді, коли стандартні функції бібліотеки не вдаються до впровадження бібліотеки, і вона нічого не може з цим зробити. Але все-таки, коли пишуть код програми, для мене це дуже зручно, а також IMHO добре виглядає просто кинутиstd::runtime_error . Також мої користувачі також можуть розраховувати на визначений мінімальний інтерфейс, наприклад, what()або коди.

І, наприклад, мій користувач подає помилкові аргументи, що було б зручніше, ніж кидати std::invalid_argument, чи не так? Так що в поєднанні з ще поширеним використанням std :: винятку, який я бачу в інших кодах: Чому б не піти далі і виходити з вашого власного класу виключень (наприклад, lib_foo_exception), а також з std::exception.

Думки?


Я не впевнений, що слідкую за цим. Просто тому , що ви успадкували від std::exceptionне означає , що ви кинутиstd::exception . Крім того, std::runtime_errorчи успадковується std::exceptionв першу чергу, і what()метод походить std::exception, не std::runtime_error. І вам обов'язково слід створити власні класи виключень, а не викидати загальні винятки, такі як std::runtime_error.
Вінсент Савард

3
Різниця полягає в тому, що коли мій lib_foo_exceptionклас походить std::exception, користувач бібліотеки може спіймати lib_foo_exception, просто ловлячи std::exception, крім того, коли він ловить лише бібліотеку. Тож я також міг би запитати, чи повинен мої бібліотеки виключення root клас успадковувати від std :: виключення .
Суперлоккус

3
@LightnessRacesinOrbit Я маю на увазі "... крім", наприклад "Скільки способів ловити lib_foo_exception?" З успадкуванням від std::exceptionвас це можна зробити catch(std::exception)АБО by catch(lib_foo_exception). Не виходячи з цього std::exception, ви б його зловити, якби, і лише тоді , якщоcatch(lib_foo_exception) .
Суперлоккус

2
@Superlokkus: Ми начебто ігноруємо catch(...). Це є тому, що мова дозволяє використовувати випадок, який ви розглядаєте (і для "неправильно поводжуваних" бібліотек), але це не найкраща сучасна практика.
Гонки легкості з Монікою

1
Багато конструкцій обробки винятків у C ++, як правило, заохочують більш грубі, більш загальні catchсайти, а також більш грубі транзакції, що моделюють операцію з кінця користувача. Якщо порівнювати його з мовами, які не сприяють ідеї узагальненого лову std::exception&, наприклад, вони часто мають набагато більше коду з посередницькими try/catchблоками, пов'язаними з дуже конкретними помилками, що дещо зменшує загальність поводження з винятками, коли вона починає розміщуватися набагато сильніше наголосити на ручному поводженні з помилками, а також на всіх різних помилках, які могли виникнути.

Відповіді:


29

Усі винятки повинні бути успадковані від std::exception.

Припустимо, наприклад, мені потрібно зателефонувати ComplexOperationThatCouldFailABunchOfWays(), і я хочу опрацювати будь-які винятки, які це може кинути. Якщо все успадковується від std::exceptionцього, це легко. Мені потрібен лише один catchблок, і у мене є стандартний інтерфейс ( what()) для отримання деталей.

try {
    ComplexOperationThatCouldFailABunchOfWays();
} catch (std::exception& e) {
    cerr << e.what() << endl;
}

Якщо винятки НЕ успадковуються std::exception, це стає набагато гірше:

try {
    ComplexOperationThatCouldFailABunchOfWays();
} catch (std::exception& e) {
    cerr << e.what() << endl;
} catch (Exception& e) {
    cerr << e.Message << endl;
} catch (framework_exception& e) {
    cerr << e.Details() << endl;
}

Щодо того, кидати runtime_errorчи invalid_argumentстворювати власні std::exceptionпідкласи для кидання: Моє правило полягає в тому, щоб запроваджувати новий підклас, коли мені потрібно обробляти певний тип помилок інакше, ніж інші помилки (тобто, коли мені потрібен окремий catchблок).

  • Якщо я введу новий підклас винятків для кожного мислимого типу помилок, навіть якщо мені не потрібно обробляти їх окремо, це додасть великої кількості розповсюдження класу.
  • Якщо я повторно використовую існуючі підкласи, щоб означати щось конкретне (тобто, якщо runtime_errorкинутий тут означає щось інше, ніж загальну помилку виконання), то я ризикую конфліктувати з іншими способами використання існуючого підкласу.
  • Якщо мені не потрібно спеціально обробляти помилку, і якщо помилка, яку я викидаю, точно відповідає одній із помилок існуючої стандартної бібліотеки (наприклад, invalid_argument), я повторно використовую існуючий клас. Я просто не бачу великої користі від додавання нового класу в цьому випадку. (Основні інструкції C ++ тут ​​не згодні зі мною - вони рекомендують завжди використовувати власні заняття.)

В Основних принципах C ++ продовжити обговорення і приклади.


Усі ці амперсанди! C ++ - дивно.
SuperJedi224

2
@ SuperJedi224 і всі ті різні листи! Англійська мова дивна.
johannes

Це обґрунтування не має для мене сенсу. Хіба це не для чого catch (...)(з буквальним еліпсисом)?
Maxpm

1
@Maxpm catch (...)корисний лише в тому випадку, якщо вам не потрібно нічого робити з кинутим. Якщо ви хочете щось зробити - наприклад, показати або записати певне повідомлення про помилку, як у моєму прикладі - тоді вам потрібно знати, що це таке.
Джош Келлі

9

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

Це неправильне припущення.

Стандартні типи винятків передбачені для використання у простому середовищі. Вони розраховані не тільки використання стандартної бібліотеки.

Так, змусити все в кінцевому рахунку успадкувати від std::exception. Часто це стосується спадкування від std::runtime_errorабо std::logic_error. Те, що підходить для винятку класу, який ви реалізуєте.

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

Якщо говорити особисто, я часто просто кидаю std::runtime_errorі закінчуюсь. Але це вступає в дискусію про те, як детально складати класи виключень, про що ви не запитуєте.

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