Спецификация исключений функций и стандартные исключения - foo () throw (Exception) - PullRequest
4 голосов
/ 23 февраля 2012

В C ++ вы можете объявить функцию со спецификацией исключения следующим образом:

int foo() const throw(Exception);

Я нашел эти две ссылки:

Но некоторые вещи остаются без ответа ...

Вопрос 1: зачем добавлять спецификацию исключений? Принесет ли это какое-либо увеличение производительности? Что будет отличаться для компилятора? Потому что это похоже на информацию для программиста.

Вопрос 2: что произойдет (что должно произойти), если я брошу что-то, что не соответствует спецификации? Например:

int foo() throw(int) {
        throw char; // Totally unrelated classes, not types in real
}

Вопрос 3: функция / метод не должна ничего бросать. Я нашел, по крайней мере, два (три, альтернативных синтаксиса для разных компиляторов) способов, чтобы не вызывать исключения:

  • int foo() throw();
  • int foo() __attribute(nothrow)__ для gcc
  • int foo() nothrow для визуального C ++

Какой из них "правильный"? Есть ли разница? Какой из них мне следует использовать?

Вопрос 4: «Стандартные исключения», bad_alloc, bad_cast, bad_exception, bad_typeid и ios_base::failure.

Хорошо bad_alloc говорит само за себя, и я знаю, как (и, что более важно, когда) его использовать (добавить в спецификацию исключений), но как насчет других? Никто из них на самом деле не звонит в колокол ... С какими "частями кода" они связаны? Как bad_alloc связано с new char[500000].

Вопрос 5: Если у меня есть иерархия классов исключений, например:

    class ExceptionFileType {
             virtual const char * getError() const = 0;
    };

    class ExceptionFileTypeMissing : public ExceptionFileType {
            virtual const char *getError() cosnt {
                    return "Missing file";
            }
    }

Должен ли я использовать:

    int foo() throw(ExceptionFileType);

Или:

    int foo() throw(ExceptionFileTypeMissing,ExceptionFileTypeNotWritable,ExceptionFileTypeNotReadable,...)

Примечание: ответы со ссылками были бы отличными. Я ищу советы по хорошей практике.

Ответы [ 4 ]

6 голосов
/ 23 февраля 2012

Простой совет "хорошей практики": не используйте спецификации исключений.

По сути, единственным исключением является возможность пустой спецификации исключения: throw(). Это достаточно полезно, так как в C ++ 11 ему было присвоено собственное ключевое слово (noexcept). Все согласны с тем, что любая непустая спецификация исключений является паршивой идеей.

Спецификации исключений (кроме noexcept) официально устарели - и в отличие от многих устаревших функций, удаление этого могло бы повлиять на достаточно мало исходного кода, и я думаю, что есть большая вероятность, что он в конечном итоге будет удален (конечно, без гарантии, во всяком случае, довольно справедливый шанс).

Что касается того, что произойдет, если / если вы выдадите исключение типа, не разрешенного спецификацией исключения: std::unexpected() вызывается. По умолчанию это вызывает terminate(). Вы можете использовать std::set_unexpected, чтобы установить свой собственный обработчик - но все, что вы можете разумно сделать, это добавить некоторые записи перед вами terminate(). Ваш неожиданный обработчик не может вернуться.

3 голосов
/ 23 февраля 2012

Вопрос 1

Не беспокойтесь.Они были плохой идеей и были объявлены устаревшими в последней версии языка.Они не дают никакой пользы компилятору, так как они проверяются во время выполнения;во всяком случае, они могут снизить производительность в некоторых случаях.

Вопрос 2

Будет вызвана функция с именем std::unexpected.По умолчанию это вызывает std::terminate;по умолчанию это завершает программу.Оба эти поведения можно изменить, используя std::set_unexpected и std::set_terminate для установки собственного обработчика, если вы действительно этого хотите.

Вопрос 3

throw() был стандартный способ сделать это;остальные являются непереносимыми расширениями компилятора.В C ++ 11 вы можете использовать noexcept, который дает проверку во время компиляции, которую ничего не может сгенерировать, вместо проверки во время выполнения, которую ничего не выдает.

Вопрос 4

  • bad_cast выбрасывается при сбое dynamic_cast ссылки.
  • bad_exception выбрасывается в некоторых странных обстоятельствах, когда нарушается спецификация исключения.
  • bad_typeid генерируется, если вычисление аргумента в typeid включает разыменование нулевого указателя
  • ios_base::failure генерируется библиотекой ввода / вывода (<iostream> и т. Д.), Когда некоторые операции завершаются неудачно

Вопрос 5

Если вы хотите разрешить выбрасывание всей иерархии, просто укажите базовый класс.Но вы вообще не должны использовать спецификаторы исключений.

2 голосов
/ 23 февраля 2012

Во-первых, давайте очень четко объясним, что делает спецификация исключения: это более или менее похоже на assert, который нельзя отключить, утверждая, что вы не выйдете из функции с исключением прочее , чемте, которые отсутствуют.Таким образом, его полезность гораздо более ограничена, чем может показаться на первый взгляд;по большей части (и в этом случае я не могу представить себе исключение) единственной действительно полезной гарантией является throw(), которая гарантирует, что исключение не будет выдано;если вы хотите написать безопасный код исключения, вам понадобится эта гарантия для нескольких низкоуровневых функций.

На практике, хотя throw() может позволить некоторые дополнительные оптимизации компилятора, общая реализация имеет тенденцию приводить к меньшемуэффективный код, когда используется спецификация исключения.В C ++ 11 throw() был заменен на noexcept, вероятно, с надеждой, что разработчики компилятора сделают с ним что-то умное.

РЕДАКТИРОВАТЬ:

Поскольку все (включаяя), кажется, пропустил ваш вопрос 4:

bad_alloc будет выброшен функцией operator new, если она не может выделить память.

bad_cast будет выброшен dynamic_cast на ссылку, в случае неудачного приведения.(A dynamic_cast для указателя возвращает нулевой указатель в таких случаях.)

bad_exception будет выброшено при нарушении спецификации исключения, при условии, что спецификация исключения допускает bad_exception.(Другими словами, забудьте об этом.)

bad_typeid будет брошено, если вы попытаетесь использовать typeid с нулевым указателем.

ios_base::failure будет брошено, если вы запроситепоток, который нужно выдать в случае ошибок.

Практически: вы поймаете bad_alloc, если захотите восстановить и продолжить из нехватки памяти.Что значит не очень часто.(Это очень, очень трудно восстановить из ситуации нехватки памяти.) Для bad_cast, вероятно, предпочтительнее использовать указатели и проверять на ноль, если вы не уверены.И нет никакого оправдания для того, чтобы когда-либо видеть bad_typeid.В большинстве случаев вы, вероятно, захотите явно тестировать ошибки ввода-вывода, а не настраивать поток для выдачи исключений;и исключение, когда установлено ios_base::badbit, может быть исключением (поскольку оно представляет собой действительно исключительный случай жесткого сбоя).

1 голос
/ 23 февраля 2012

Вопросы 1 и 2 подробно рассматриваются в этом вопросе .

Вопросы 3 и 5 охватываются рекомендацией в принятом ответе на этот вопрос, что вы вообще не используете спецификации исключений.

Вопрос 4, кажется, адекватно решается путем ввода этих имен исключений в поисковую систему по вашему выбору или обращения к указателю хорошей книги по С ++. У вас есть конкретный запрос о них?

...