Різниця: std :: runtime_error vs std :: виключення ()


127

У чому різниця між std::runtime_errorі std::exception? Що для кожного підходить? Чому вони відрізняються в першу чергу?

Відповіді:


152

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

std::runtime_error- це більш спеціалізований клас, який походить від std::exception, призначений для викидання у випадку різних помилок виконання . Він має подвійне призначення. Це може бути кинуто сам по собі, або вона може служити в якості базового класу до різних ще більш спеціалізованим типам винятків помилок у час виконання, наприклад std::range_error, і std::overflow_errorт.д. Ви можете визначити свої власні класи виключень сходячи з std::runtime_error, а також ви можете визначити свої власні виключення класи, що походять від с std::exception.

Так само, як std::runtime_errorі стандартна бібліотека std::logic_error, яка також походить від std::exception.

Сенс цієї ієрархії полягає в наданні користувачеві можливості використовувати всю потужність механізму обробки винятків C ++. Оскільки застереження "catch" може охоплювати поліморфні винятки, користувач може записати пропозиції "catch", які можуть вловлювати типи винятків з певного піддерева ієрархії винятків. Наприклад, catch (std::runtime_error& e)зловить усі винятки з std::runtime_errorпіддерева, дозволяючи всім іншим пройти (і пролетіти далі вгору до стека викликів).

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

Оновлення: портативність Linux проти Windows

Як зазначили Локі Астарі та unixman83 у своїй відповіді та коментарях нижче, конструктор exceptionкласу не приймає жодних аргументів відповідно до стандарту C ++. У Microsoft C ++ є конструктор, який бере аргументи в exceptionкласі, але це не стандартно. runtime_errorКлас має конструктор , який приймає аргументи ( char*) на обох платформах, Windows , і Linux. Щоб бути портативним, краще використовуйте runtime_error.

(І пам’ятайте, тільки тому, що специфікація вашого проекту говорить, що ваш код не повинен працювати в Linux, це не означає, що він ніколи не повинен працювати в Linux.)


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

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

1
Як і вбік: ніде не існує правила, яке змушує вас виходити std::exception. Звичайно, всі stdречі кидають похідні класи цього, але немає абсолютно ніяких причин лише кидати std::exceptionпохідні об'єкти.
rubenvb

1
@rubenvb Я про це не знав, але думаю, що він очистить код для подальшого обслуговування, якщо будуть кинуті лише об'єкти класів, похідні від винятку. Приклад: мені подобається дізнатися, які спеціальні винятки реалізовані в моїй кодовій базі та шукати класи, похідні від винятку.
this.myself

21

std::exceptionслід вважати (зауважте, що розглядається) абстрактною базою стандартної ієрархії винятків. Це тому, що не існує механізму для передачі певного повідомлення (для цього потрібно вивести і спеціалізуватися what()). Ніщо не заважає вам використовувати std :: виключення, і для простих додатків це може бути все, що вам потрібно.

std::runtime_errorз іншого боку, є дійсні конструктори, які приймають рядок як повідомлення. Коли what()викликається вказівник const char, повертається, що вказує на рядок C, що має ту саму рядок, що була передана в конструктор.

try
{
    if (badThingHappened)
    {
         throw std::runtime_error("Something Bad happened here");
    }
}
catch(std::exception const& e)
{
    std::cout << "Exception: " << e.what() << "\n";
} 

1
Дякую за відповідь мартін. Однак я використовую std :: виключення () так само, як ви описали вище. тобто конструктор std :: виключення () також може приймати std :: string () або const char *.
sivabudh

14
Не за стандартом. std :: виключення має один конструктор, який не бере аргументів. Використання версії, яка приймає std :: string або C-String, не є портативною.
Мартін Йорк

10
Через Microsoft я звик кидати std::exception(std::string). Тепер я розумію, що повинен кинути, std::runtime_errorякщо хочу, щоб мій код працював у Linux (GCC).
unixman83
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.