Должен ли я использовать спецификатор исключения в C++?

StackOverflow https://stackoverflow.com/questions/88573

Вопрос

В C++ вы можете указать, что функция может или не может генерировать исключение, используя спецификатор исключения.Например:

void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type

Я сомневаюсь в их использовании по следующим причинам:

  1. Компилятор на самом деле не обеспечивает строгого соблюдения спецификаторов исключений, поэтому преимущества невелики.В идеале вы хотели бы получить ошибку компиляции.
  2. Если функция нарушает спецификатор исключения, я думаю, что стандартным поведением является завершение программы.
  3. В VS.Net throw(X) рассматривается как throw(...), поэтому соблюдение стандарта не является строгим.

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

Это было полезно?

Решение

Нет.

Вот несколько примеров, почему:

  1. Код шаблона невозможно написать со спецификациями исключений,

    template<class T>
    void f( T k )
    {
         T x( k );
         x.x();
    }
    

    Копии могут выдать ошибку, передача параметров может выдать ошибку, и x() может вызвать какое-то неизвестное исключение.

  2. Спецификации исключений имеют тенденцию запрещать расширяемость.

    virtual void open() throw( FileNotFound );
    

    может превратиться в

    virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );
    

    Вы действительно могли бы написать это как

    throw( ... )
    

    Первый не расширяем, второй слишком амбициозен, а третий — именно то, что вы имеете в виду, когда пишете виртуальные функции.

  3. Устаревший код

    Когда вы пишете код, основанный на другой библиотеке, вы не знаете, что она может сделать, если что-то пойдет не так.

    int lib_f();
    
    void g() throw( k_too_small_exception )
    { 
       int k = lib_f();
       if( k < 0 ) throw k_too_small_exception();
    }
    

    g прекратится, когда lib_f() бросает.Это (в большинстве случаев) не то, чего вы действительно хотите. std::terminate() никогда не следует звонить.Всегда лучше позволить приложению аварийно завершить работу с необработанным исключением, из которого можно получить трассировку стека, чем тихо/насильственно умереть.

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

    Error e = open( "bla.txt" );
    if( e == FileNotFound )
        MessageUser( "File bla.txt not found" );
    if( e == AccessDenied )
        MessageUser( "Failed to open bla.txt, because we don't have read rights ..." );
    if( e != Success )
        MessageUser( "Failed due to some other error, error code = " + itoa( e ) );
    
    try
    {
       std::vector<TObj> k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    { 
       MessageUser( "out of memory, exiting process" );
       throw;
    }
    

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

Другие советы

Избегайте спецификаций исключений в C++.Причины, которые вы указываете в своем вопросе, являются довольно хорошим началом.

См. Херба Саттера «Прагматичный взгляд на спецификации исключений».

Я думаю, что стандартное соглашение кроме (для C++)
Спецификаторы исключений были экспериментом в стандарте C++, который по большей части провалился.
Исключением является то, что спецификатор no throw полезен, но вам также следует добавить соответствующий блок try catch внутри, чтобы убедиться, что код соответствует спецификатору.У Херба Саттера есть страница на эту тему. Готч 82

В дополнение думаю стоит описать Exception Guarantees.

По сути, это документация о том, как на состояние объекта влияют исключения, ускользающие от метода этого объекта.К сожалению, компилятор не соблюдает их и не упоминает иным образом.
Увеличение и исключения

Гарантии исключений

Без гарантии:

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

Основная гарантия:

Почти во всех ситуациях это должна быть минимальная гарантия, которую дает метод.
Это гарантирует, что состояние объекта четко определено и его можно последовательно использовать.

Сильная гарантия:(также известная как транзакционная гарантия)

Это гарантирует успешное завершение метода.
Или будет выброшено исключение, и состояние объектов не изменится.

Нет гарантии броска:

Метод гарантирует, что никакие исключения не могут распространяться за пределы метода.
Все деструкторы должны давать эту гарантию.
| НбЕсли исключение выходит из деструктора, когда исключение уже распространяется
| Приложение прекратит

gcc будет выдавать предупреждения, если вы нарушите спецификации исключений.Что я делаю, так это использую макросы для использования спецификаций исключений только в режиме «lint», компилируя специально для проверки соответствия исключений моей документации.

Единственный полезный спецификатор исключения — это «throw()», то есть «не выбрасывает».

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

Кроме того, я считаю, что их использование устарело в текущих проектах стандарта C++0x.

Спецификации исключений не являются очень полезными инструментами в C++.Однако им /есть/ хорошее применение в сочетании с std::unexpected.

В некоторых проектах я кодирую код со спецификациями исключений, а затем вызываю set_unexpected() с функцией, которая выдает специальное исключение моей собственной разработки.Это исключение при создании получает обратную трассировку (в зависимости от платформы) и является производным от std::bad_Exceptionion (чтобы его можно было распространять при желании).Если это вызывает вызов метода завершения(), как это обычно бывает, обратная трассировка выводится методом What() (а также исходное исключение, вызвавшее его;не так уж сложно это найти), и поэтому я получаю информацию о том, где был нарушен мой контракт, например, какое неожиданное исключение библиотеки было выброшено.

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

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

В противном случае я не считаю особенно полезным использовать что-либо, кроме throw() чтобы указать, что он не генерирует никаких исключений.

Спецификация "throw()" позволяет компилятору выполнять некоторые оптимизации при анализе потока кода, если он знает, что функция никогда не будет генерировать исключение (или, по крайней мере, обещает никогда не генерировать исключение).Ларри Остерман кратко рассказывает об этом здесь:

http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx

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

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

Я также отмечу, что Скотт Мейерс рассматривает эту тему в книге «Более эффективный C++».Его книги «Эффективный C++» и «Более эффективный C++» настоятельно рекомендуются.

Да, если вы разбираетесь во внутренней документации.Или, может быть, написать библиотеку, которую будут использовать другие, чтобы они могли сказать, что происходит, не обращаясь к документации.Выброс или невыброс можно считать частью API, почти как возвращаемое значение.

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

Они могут быть полезны для модульного тестирования, чтобы при написании тестов вы знали, что ожидать от функции в случае сбоя, но в компиляторе к ним не применяются никакие ограничения.Я думаю, что это дополнительный код, который не нужен в C++.Что бы вы ни выбрали, все, в чем вы должны быть уверены, — это то, что вы следуете одному и тому же стандарту кодирования для всего проекта и членов команды, чтобы ваш код оставался читабельным.

Из статьи:

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

«Хорошо известно, что невозможно написать общий контейнер, защищенный от исключений». Это утверждение часто рассматривается со ссылкой на статью Тома Каргилла [4], в которой он исследует проблему безопасности исключений для общего шаблона стека.В своей статье Каргилл поднимает много полезных вопросов, но, к сожалению, не может представить решение своей проблемы.1 Он заключает, предполагая, что решение может быть невозможно.К сожалению, его статья была прочитана многими как «доказательство» этого предположения.С тех пор, как он был опубликован, было показано много примеров общих компонентов, безопасных исключений, среди которых стандартные библиотечные контейнеры C ++.

И действительно, я могу придумать способы сделать исключения классов шаблонов безопасными.Если у вас нет контроля над всеми подклассами, у вас все равно могут возникнуть проблемы.Для этого можно создать в ваших классах определения типов, определяющие исключения, создаваемые различными классами шаблонов.Я думаю, что проблема, как всегда, в том, чтобы прикрепить это позже, а не проектировать с самого начала, и я думаю, что именно эти накладные расходы являются настоящим препятствием.

Спецификации исключений = чушь, спросите любого Java-разработчика старше 30 лет.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top