C ++ - Аргументы для исключений над кодами возврата - PullRequest
29 голосов
/ 04 декабря 2009

У меня дискуссия о том, как идти в новом проекте C ++. Я предпочитаю исключения перед кодами возврата (только для исключительных случаев) по следующим причинам -

  1. Конструкторы не могут дать код возврата
  2. Разъединяет путь отказа (который должен быть очень редким) из более чистого логического кода
  3. Быстрее в неисключительном случае (без проверки, если / иначе сотни тысяч раз)
  4. Если кто-то испортит настройки кода возврата (забудет вернуть FAIL), может потребоваться очень много времени для поиска.
  5. Лучшая информация из сообщения, содержащегося в ошибке. (Мне было указано, что возвращаемое перечисление может сделать то же самое для кодов ошибок)
  6. От Джареда Пар Невозможно игнорировать без кода, специально разработанного для его обработки

Это пункты, которые я придумал, подумав об этом, и из поисков в Google. Я должен признать, что был предрасположен к исключениям, работавшим в C # в течение последних нескольких лет. Пожалуйста, опишите дальнейшие причины использования исключений по кодам возврата. Для тех, кто предпочитает коды возврата, я также хотел бы выслушать ваши рассуждения. Спасибо

Ответы [ 11 ]

45 голосов
/ 04 декабря 2009

Я думаю, что статья подводит итог.

Аргументы для использования исключений

  1. Исключения отделяют код обработки ошибок от обычного программного потока и, таким образом, делают код более читабельным, надежным и расширяемым.
  2. Создание исключения - единственный чистый способ сообщить об ошибке из конструктора.
  3. Исключения трудно игнорировать, в отличие от кодов ошибок.
  4. Исключения легко распространяются из глубоко вложенных функций.
  5. Исключениями могут быть и часто являются определяемые пользователем типы, которые несут гораздо больше информации, чем код ошибки.
  6. Объекты исключений сопоставляются с обработчиками с использованием системы типов.

Аргументы против использования исключений

  1. Исключения нарушают структуру кода, создавая несколько невидимых точек выхода, которые затрудняют чтение и проверку кода.
  2. Исключения легко приводят к утечке ресурсов, особенно в языке, в котором нет встроенного сборщика мусора и, наконец, блоков.
  3. Научиться писать безопасный код исключений сложно.
  4. Исключения являются дорогостоящими и нарушают обещание платить только за то, что мы используем.
  5. Исключения трудно представить в устаревшем коде.
  6. Исключения легко используются для выполнения задач, которые относятся к обычному потоку программ.
10 голосов
/ 04 декабря 2009

Лучший случай, который я слышал о предпочтении кодов возврата, а не исключений, это просто:

  1. Написание исключительного безопасного кода: hard [in C ++].

Имея большой опыт работы в C #, я могу сочувствовать вашему желанию использовать исключения, но, к сожалению, C ++ не является C #, и многие вещи, с которыми мы можем справиться в C #, могут быть смертельно опасными в C ++.

Хорошее суммирование аргументов за и против можно найти в Руководства по стилю Google . Короче говоря:

Плюсы:

  • Исключения позволяют более высоким уровням приложения решать, как обрабатывать "не может случиться" неудачи в глубоко вложенные функции, без скрывающая и подверженная ошибкам бухгалтерия кодов ошибок.
  • Исключения используются большинством других современных языков. Используя их в C ++ сделает его более совместимым с Python, Java и C ++, которые другие знакомы с.
  • Некоторые сторонние библиотеки C ++ используют исключения и отключают их внутренне затрудняет интегрировать с этими библиотеками.
  • Исключения - единственный способ для конструктора потерпеть неудачу. Мы можем симулировать это с заводской функцией или Метод Init (), но для этого требуется куча распределение или новое «недействительное» состояние, соответственно.
  • Исключения действительно удобны при тестировании фреймворков.

Минусы:

  • Когда вы добавляете оператор throw к существующей функции, вы должны изучить все его переходные абоненты. Либо они должны сделать по крайней мере гарантия безопасности основного исключения, или они никогда не должны ловить исключение и быть счастливым с программой завершается в результате. Например, если f () вызывает g (), то h () и h выдает исключение, что f ловит, г должен быть осторожен, иначе он не может быть чистым правильно.
  • В целом, исключения затрудняют управление программами оценить, посмотрев на код: функции могут вернуться в тех местах, где вы не ожидай Это результаты ремонтопригодность и отладка трудности. Вы можете минимизировать это стоимость через некоторые правила о том, как и где исключения могут быть использованы, но на стоимость больше, что нужно разработчику знать и понимать.
  • Исключительная безопасность требует как RAII, так и различных методов кодирования. Требуется много вспомогательного оборудования сделать запись правильной исключительной безопасности код легко. Кроме того, чтобы избежать необходимости читатели, чтобы понять весь призыв граф, исключительный код должен изолировать логику, которая пишет постоянное состояние в "коммит" фаза. Это будет иметь оба преимущества и расходы (возможно, где вы вынуждены запутать код, чтобы изолировать фиксации). Разрешить исключения будет заставить нас всегда оплачивать эти расходы даже если они того не стоят.
  • Включение исключений добавляет данные к каждому произведенному двоичному файлу, увеличивая время компиляции (возможно, немного) и возможно увеличение адресного пространства давление.
  • Наличие исключений может побудить разработчиков бросать их когда они не подходят или оправиться от них, когда это не безопасно сделать это. Например, недопустимый пользователь ввод не должен вызывать исключения быть брошенным. Нам нужно было бы сделать руководство по стилю еще дольше документировать эти ограничения!

Я предлагаю прочитать и понять все «за» и «против», а затем принять решение для вашего собственного проекта, основываясь на них. У вас нет того же программного обеспечения, что у Google, поэтому то, что имеет для них смысл, может не иметь смысла для вас (вот почему я опускаю их заключение).

6 голосов
/ 04 декабря 2009

ИМХО, причина № 1, чтобы отдавать предпочтение исключениям по сравнению с кодами возврата, заключается в том, что вы не можете молча игнорировать исключение. Для этого требуется как минимум минимальное количество дополнительного кода.

4 голосов
/ 05 декабря 2009

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

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

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

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

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

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

3 голосов
/ 05 декабря 2009

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

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

long Foo( )
{
    long retCode = MoveStage( );
    if ( retCode != RetCode_OK )
    {
        logger->LogError( __LINE__, __FILE__, "some message" );
        return( retCode );
    }

    int someConfigVar = 0;
    long retCode = GetConfigVar( "SomeVarName", someConfigVar );
    if ( retCode != RetCode_OK )
    {
        logger->LogError( __LINE__, __FILE__, "some message" );
        return( retCode );
    }

    long retCode = DoSomething( );
    if ( retCode != RetCode_OK )
    {
        logger->LogError( __LINE__, __FILE__, "some message" );
        return( retCode );
    }

    // and on and on and on...
}

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

3 голосов
/ 05 декабря 2009

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

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

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

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

Сделайте это, используя соответствующие исключения.

3 голосов
/ 04 декабря 2009

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

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

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

3 голосов
/ 04 декабря 2009

Быстрее в неисключительном случае (без проверки, если / иначе сотни тысяч раз)

В неисключительном случае это единственное сравнение, чтобы определить, что E_SUCCESS был возвращен.

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

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

2 голосов
/ 04 декабря 2009

Очень сложно написать безопасный код исключения. Полностью надуманный пример: -

void Class::Method()
{ 
  i++;
  SomeCallThatMightThrow();
  j++;
}

Замените i ++ и j ++ любыми двумя переменными, ресурсами, состояниями, которые должны оставаться синхронными. Сборка мусора избавила нас от необходимости запоминать для сопряжения наших новых и удаления. По иронии судьбы старомодное явное тестирование кода возврата избавляет нас от необходимости тщательно анализировать каждую функцию, которая может выдавать исключения, чтобы убедиться, что они не связаны с постусловиями.

1 голос
/ 04 декабря 2009

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

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

Кроме того, были показаны исключения, когда я измерил их на много-много порядков медленнее по сравнению с простым кодом возврата.

Ну тогда они говорят, что вы должны бросать только в исключительных обстоятельствах, но как вы сообщаете о неисключительных, ожидаемых, часто встречающихся ошибках. Ну и коды возврата конечно! :)

Я не понимаю, насколько это выгодно.

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