В C ++ каковы преимущества использования исключений и try / catch вместо простого возврата кода ошибки?

См. также: алгоритм суммирования Kahan Он не требует хранения O (n), а только O (1).

23
задан KPexEA 13 October 2008 в 02:11
поделиться

13 ответов

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

Исключение нужно каким-то образом признать - его нельзя игнорировать, не активируя что-либо для этого.

29
ответ дан Martin 13 October 2008 в 02:11
поделиться

Преимущество исключений в два раза:

  • Их нельзя игнорировать. Вы должны иметь дело с ними на каком-то уровне, или они прекратят вашу программу. С кодом ошибки вы должны явно проверить их, иначе они будут потеряны.

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

23
ответ дан James Curran 13 October 2008 в 02:11
поделиться

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

С сообщениями об ошибках:

int DoSomeThings()
{
    int error = 0;
    HandleA hA;
    error = CreateAObject(&ha);
    if (error)
       goto cleanUpFailedA;

    HandleB hB;
    error = CreateBObjectWithA(hA, &hB);
    if (error)
       goto cleanUpFailedB;

    HandleC hC;
    error = CreateCObjectWithA(hB, &hC);
    if (error)
       goto cleanUpFailedC;

    ...

    cleanUpFailedC:
       DeleteCObject(hC);
    cleanUpFailedB:
       DeleteBObject(hB);
    cleanUpFailedA:
       DeleteAObject(hA);

    return error;
}

С исключениями и RAII

void DoSomeThings()
{
    RAIIHandleA hA = CreateAObject();
    RAIIHandleB hB = CreateBObjectWithA(hA);
    RAIIHandleC hC = CreateCObjectWithB(hB);
    ...
}

struct RAIIHandleA
{
    HandleA Handle;
    RAIIHandleA(HandleA handle) : Handle(handle) {}
    ~RAIIHandleA() { DeleteAObject(Handle); }
}
...

На первый взгляд версия RAII / Исключения выглядит длиннее, пока вы не поймете, что код очистки должен быть написано только один раз (и есть способы упростить это). Но вторая версия DoSomeThings намного понятнее и удобнее в обслуживании.

НЕ пытайтесь использовать исключения в C ++ без языка RAII, так как это приведет к утечке ресурсов и памяти. Вся ваша очистка должна быть сделана в деструкторах объектов, выделенных стеком.

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

Одним из пунктов кодов ошибок является то, что они делают очевидным, где что-то может выйти из строя и как оно может выйти из строя. В приведенном выше коде вы пишете его с предположением, что что-то не выйдет из строя (но если они это сделают, вы будете защищены оболочками RAII). Но вы в конечном итоге уделяете меньше внимания тому, где что-то может пойти не так.

17
ответ дан Eclipse 13 October 2008 в 02:11
поделиться

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

10
ответ дан Bill the Lizard 13 October 2008 в 02:11
поделиться

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

8
ответ дан Gerald 13 October 2008 в 02:11
поделиться
  • возвращают код ошибки, когда ожидается состояние ошибки , в некоторых случаях
  • выдают исключение, когда не ожидается состояние ошибки .

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

7
ответ дан Steven A. Lowe 13 October 2008 в 02:11
поделиться

Я написал запись в блоге об этом ( Исключения составляют элегантный код ), которая впоследствии была опубликована в Перегрузка . Я действительно написал это в ответ на то, что Джоэл сказал в подкасте StackOverflow!

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

Боль проверки кодов ошибок означает, что люди часто пренебрегают этим, делая их совершенно бессмысленными: по крайней мере, вы должны явно игнорировать исключения с помощью оператора catch.

Использование деструкторов в C ++ и диспозиторов в .NET для обеспечения правильного освобождения ресурсов при наличии исключений также может значительно упростить код. Чтобы получить тот же уровень защиты с помощью кодов ошибок, вам нужно либо много операторов if, много дублированного кода очистки, либо goto вызовов общего блока очистки в конце функции. Ни один из этих вариантов не приятен.

5
ответ дан Anthony Williams 13 October 2008 в 02:11
поделиться

Вот хорошее объяснение EAFP («Проще просить прощения, чем разрешения»), которое, я думаю, применимо здесь, даже если это страница Python в Википедии. Использование исключений приводит к более естественному стилю кодирования, IMO - и, по мнению многих других, тоже.

2
ответ дан Kevin Little 13 October 2008 в 02:11
поделиться

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

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

Связанное с этим преимущество, конечно, заключается в том, что вы не «тратите» возвращаемое значение на исключения (и, таким образом, допускаете, чтобы методы, которые должны быть недействительными, считались недействительными), а также можете возвращать ошибки от конструкторов и деструкторов.

2
ответ дан Uri 13 October 2008 в 02:11
поделиться

Иногда вам действительно нужно использовать исключение, чтобы отметить исключительный случай. Например, если что-то пойдет не так в конструкторе, и вы обнаружите, что имеет смысл уведомить вызывающего об этом, у вас нет другого выбора, кроме как сгенерировать исключение.

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

int divide(int a, int b)
{
    if( b == 0 )
        // then what?  no integer can be used for an error flag!
    else
        return a / b;
}
1
ответ дан wilhelmtell 13 October 2008 в 02:11
поделиться

Руководство по стилю Google C ++ содержит большой и тщательный анализ плюсов и минусов использования исключений в коде C ++. Это также указывает на некоторые более крупные вопросы, которые вы должны задавать; то есть я собираюсь распространять свой код среди других (у кого могут возникнуть трудности при интеграции с кодовой базой с поддержкой исключений)?

2
ответ дан cdleary 13 October 2008 в 02:11
поделиться

Как отметил @Martin, создание исключений заставляет программиста обрабатывать ошибку. Например, не проверка кодов возврата является одним из самых больших источников дыр в безопасности в программах на Си. Исключения гарантируют, что вы обработаете ошибку (надеюсь) и предоставите какой-нибудь путь восстановления для вашей программы. И если вы решите игнорировать исключение, а не вводить дыру в безопасности, ваша программа вылетает.

0
ответ дан Mark Kegel 13 October 2008 в 02:11
поделиться

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

Простой вызов IsOk для объекта ошибки, не реагируя на него, будет эквивалентен записи catch (...) {} Оба выражения будут демонстрировать одинаковое отсутствие доброй воли программиста.

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

1
ответ дан QBziZ 13 October 2008 в 02:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: