Я обычно добавляю множество утверждений в свой код на C ++, чтобы упростить отладку, не влияя на производительность сборок выпуска. Теперь assert
это чистый C макро разработан без механизмов C ++ в виду.
C ++, с другой стороны, определяет std::logic_error
, что должно быть выброшено в случаях, когда есть ошибка в логике программы (отсюда и название). Создание экземпляра может быть просто идеальной альтернативой в стиле C ++ assert
.
Проблема заключается в том, что assert
и abort
как завершить программу сразу без вызова деструкторов, поэтому пропуск очистки, в то время как бросать исключение вручную добавляет ненужные затраты времени выполнения. Один из способов обойти это - создать собственный макрос утверждения SAFE_ASSERT
, который работает так же, как аналог C, но выдает исключение при ошибке.
Я могу придумать три мнения по этой проблеме:
- Придерживайтесь утверждения С. Поскольку программа завершается немедленно, не имеет значения, правильно ли развернуты изменения. Кроме того, использование
#define
s в C ++ так же плохо. - Бросить исключение и поймать его в main () . Разрешение коду пропускать деструкторы в любом состоянии программы является плохой практикой, и ее следует избегать любой ценой, как и вызовов terminate (). Если выбрасываются исключения, их нужно перехватывать.
- Вызвать исключение и позволить ему завершить программу. Исключение, завершающее программу, - это нормально, и поэтому
NDEBUG
этого никогда не произойдет в сборке выпуска. В захвате нет необходимости, и он раскрывает детали реализации внутреннего кодаmain()
.
Есть ли однозначный ответ на эту проблему? Любая профессиональная рекомендация?
Отредактировано: пропуск деструкторов, конечно, не является неопределенным поведением.
источник
logic_error
это логическая ошибка. Ошибка в логике программы называется ошибкой. Вы не решаете ошибки, выбрасывая исключения.static_assert
там, где это уместно, если оно у вас есть.std::bug
?std::abort()
; он просто вызовет сигнал, который приведет к завершению процесса.Ответы:
Утверждения полностью уместны в коде C ++. Исключения и другие механизмы обработки ошибок на самом деле не предназначены для того же, что и утверждения.
Обработка ошибок предназначена для случаев, когда есть возможность исправить ошибку или сообщить пользователю об ошибке. Например, если при чтении входного файла произошла ошибка, вы можете что-то с этим сделать. Ошибки могут возникать из-за ошибок, но они также могут быть просто подходящим выходом для данного входа.
Утверждения предназначены для таких вещей, как проверка того, что требования API выполняются, когда API обычно не проверяется, или для проверки вещей, которые разработчик считает гарантированными конструкцией. Например, если алгоритм требует отсортированного ввода, вы обычно не проверяете это, но у вас может быть утверждение, чтобы проверить его, чтобы отладочные сборки помечали этот тип ошибки. Утверждение всегда должно указывать на некорректно работающую программу.
Если вы пишете программу, в которой нечистое завершение работы может вызвать проблему, вы можете избегать утверждений. Неопределенное поведение строго с точки зрения языка C ++ здесь не квалифицируется как такая проблема, поскольку выполнение утверждения, вероятно, уже является результатом неопределенного поведения или нарушения какого-либо другого требования, которое может помешать правильной работе некоторой очистки.
Кроме того, если вы реализуете утверждения в терминах исключения, оно потенциально может быть поймано и «обработано», даже если это противоречит самой цели утверждения.
источник
3
вместо1
вашего кода, в общем случае это не должно вызывать утверждения. Утверждения - это только ошибка программиста, а не ошибка пользователя библиотеки или приложения.Утверждения предназначены для отладки . Пользователь вашего отправленного кода никогда не должен их видеть. Если утверждение выполнено, ваш код необходимо исправить.
CWE-617: достижимое утверждение
Исключения составляют исключительные обстоятельства . Если такое произойдет, пользователь не сможет делать то, что хочет, но сможет продолжить работу в другом месте.
Обработка ошибок предназначена для нормального выполнения программы. Например, если вы запрашиваете у пользователя номер и получаете что-то неразборчивое, это нормально , потому что ввод данных пользователем не находится под вашим контролем, и вы всегда должны обрабатывать все возможные ситуации как само собой разумеющееся. (Например, цикл до тех пор, пока у вас не будет действительного ввода, говоря «Извините, попробуйте еще раз».)
источник
Утверждения могут использоваться для проверки внутренних инвариантов реализации, таких как внутреннее состояние до или после выполнения какого-либо метода и т. Д. Если утверждение не работает, это на самом деле означает, что логика программы нарушена, и вы не можете исправить это. В этом случае лучшее, что вы можете сделать, - это как можно скорее прервать работу, не передавая пользователю исключения. Что действительно приятно в утверждениях (по крайней мере, в Linux), так это то, что дамп ядра создается в результате завершения процесса, и, таким образом, вы можете легко исследовать трассировку стека и переменные. Это гораздо более полезно для понимания логической ошибки, чем сообщение об исключении.
источник
Отсутствие деструкторов из-за alling abort () не является неопределенным поведением!
Если бы это было так, то это тоже было бы неопределенное поведение
std::terminate()
, поэтому какой смысл в его предоставлении?assert()
столь же полезен в C ++, как и в C. Утверждения предназначены не для обработки ошибок, а для немедленного прерывания программы.источник
abort()
, для немедленного прерывания программы. Вы правы, что утверждения не предназначены для обработки ошибок, но assert все же пытается обработать ошибку путем прерывания. Разве вам не следует вместо этого создать исключение и позволить вызывающей стороне обработать ошибку, если это возможно? В конце концов, вызывающий может лучше определить, делает ли сбой одной функции нецелесообразным делать что-либо еще. Возможно, вызывающий пытается выполнить три несвязанных друг с другом дела, но может завершить два других задания и просто отказаться от этого.assert
определен для вызоваabort
(когда условие ложно). Что касается исключения исключений, нет, это не всегда уместно. Некоторые вещи не могут быть выполнены вызывающим абонентом. Вызывающий не может определить, можно ли исправить логическую ошибку в функции сторонней библиотеки или исправить поврежденные данные.IMHO, утверждения предназначены для проверки условий, которые в случае нарушения делают все остальное чепухой. И поэтому вы не можете от них вылечиться, вернее, выздоровление не имеет значения.
Я бы сгруппировал их в 2 категории:
Это оба тривиальные примеры, но они не так уж далеки от реальности. Например, подумайте о наивных алгоритмах, которые возвращают отрицательные индексы для использования с векторами. Или встроенные программы в специализированное оборудование. Вернее, потому что дерьмо случается .
И если есть такие ошибки разработки, вы не должны быть уверены в том, что какой-либо механизм восстановления или обработки ошибок реализован. То же самое касается аппаратных ошибок.
источник