Разница: std :: runtime_error vs std :: exception ()

128

В чем разница между std::runtime_errorи std::exception? Как правильно использовать каждый? Почему они вообще разные?

sivabudh
источник

Ответы:

153

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класса есть конструктор, принимающий arguments ( char*) на обеих платформах, Windows и Linux. Чтобы быть портативным, лучше использовать runtime_error.

(И помните, что если в спецификации вашего проекта указано, что ваш код не должен работать в Linux, это не означает, что он никогда не должен работать в Linux.)

Муравей
источник
1
Спасибо. отличный ответ. хотя мне интересно, нужно ли когда-нибудь иметь другой тип исключения ... просто подумайте.
sivabudh
1
Если существует возможность повторного покрытия исключения, тогда может быть полезен другой тип исключения, поскольку мы можем использовать механизм обработки исключений, чтобы направить исключение обработчику, который попытается исправить проблему. Если нет шансов на восстановление, можно использовать одно из стандартных исключений.
Мартин Йорк,
1
Просто в сторону: нигде нет правила, которое заставляло бы вас исходить std::exception. Конечно, все stdвещи бросают производные классы этого, но нет абсолютно никаких причин бросать только std::exceptionпроизводные объекты.
rubenvb 07
1
@rubenvb Я не знал об этом, но я думаю, что это очистит код для будущего обслуживания, если будут выброшены только объекты классов, производных от исключения. Пример: мне нравится узнавать, какие настраиваемые исключения реализованы в моей базе кода, и искать классы, производные от исключения.
this.myself 07
21

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

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 :: exception () так же, как вы описали выше. т.е. конструктор std :: exception () также может принимать std :: string () или const char *.
sivabudh
14
Не по стандарту. std :: exception имеет один конструктор, который не принимает аргументов. Использование версии, которая принимает std :: string или C-String, непереносимо.
Мартин Йорк,
10
Из-за Microsoft я привык к бросанию std::exception(std::string). Теперь я понимаю, что должен бросить, std::runtime_errorесли хочу, чтобы мой код работал в Linux (GCC).
unixman83