Использование операторов условного ветвления для обнаружения ложного предположения, а также вывода сообщения об ошибке и завершения выполнения программы является настолько распространенным решением возникающих проблем, что C++ решил это дело упростить. И упростил он его с помощью assert.
Стейтмент assert
Стейтмент assert (или «оператор проверочного утверждения») в языке C++ — это макрос препроцессора, который обрабатывает условное выражение во время выполнения. Если условное выражение истинно, то стейтмент assert ничего не делает. Если же оно ложное, то выводится сообщение об ошибке, и программа завершается. Это сообщение об ошибке содержит ложное условное выражение, а также имя файла с кодом и номером строки с assert. Таким образом, можно легко найти и идентифицировать проблему, что очень помогает при отладке программ.
Сам assert реализован в заголовочном файле cassert и часто используется как для проверки корректности переданных параметров функции, так и для проверки возвращаемого значения функции:
1 2 3 4 5 6 7 8 9 |
#include <cassert> // для assert() int getArrayValue(const std::array<int, 10> &array, int index) { // Предполагается, что значение index-а находится между 0 и 8 assert(index >= 0 && index <= 8); // это строка 6 в Program.cpp return array[index]; } |
Если в вышеприведенной программе вызвать getArrayValue(array, -3);
, то программа выведет следующее сообщение:
Assertion failed: index >= 0 && index <=8, file C:\\VCProjects\\Program.cpp, line 6
Рекомендуется использовать стейтменты assert. Иногда утверждения assert бывают не очень описательными, например:
1 |
assert(found); |
Если этот assert сработает, то получим:
Assertion failed: found, file C:\\VCProjects\\Program.cpp, line 42
Но что это нам сообщает? Очевидно, что что-то не было найдено, но что именно? Вам нужно будет самому пройтись по коду, чтобы это определить.
К счастью, есть небольшой трюк, который можно использовать для исправления этой ситуации. Просто добавьте сообщение в качестве строки C-style вместе с логическим оператором И:
1 |
assert(found && "Animal could not be found in database"); |
Как это работает? Строка C-style всегда принимает значение true
. Поэтому, если found
примет значение false
, то false && true = false
. Если же found
примет значение true
, то true && true = true
. Таким образом, строка C-style вообще не влияет на обработку утверждения.
Однако, если assert сработает, то строка C-style будет включена в сообщение assert:
Assertion failed: found && "Animal could not be found in database", file C:\\VCProjects\\Program.cpp, line 42
Это даст дополнительное объяснение того, что пошло не так.
NDEBUG
Функция assert() тратит мало ресурсов на проверку условия. Кроме того, стейтменты assert (в идеале) никогда не должны встречаться в релизном коде (потому что ваш код к этому моменту уже должен быть тщательно протестирован). Следовательно, многие разработчики предпочитают использовать assert только в конфигурации Debug. В языке C++ есть возможность отключить все assert-ы в релизном коде — использовать директиву #define NDEBUG
:
1 2 3 |
#define NDEBUG // Все стейтменты assert будут проигнорированы аж до самого конца этого файла |
Некоторые IDE устанавливают NDEBUG
по умолчанию, как часть параметров проекта в конфигурации Release.
Обратите внимание, функция exit() и assert (если он срабатывает) немедленно прекращают выполнение программы, без возможности выполнить дальнейшую любую очистку (например, закрыть файл или базу данных). Из-за этого их следует использовать аккуратно.
static_assert
В C++11 добавили еще один тип assert-а — static_assert. В отличие от assert, который срабатывает во время выполнения программы, static_assert срабатывает во время компиляции, вызывая ошибку компилятора, если условие не является истинным. Если условие ложное, то выводится диагностическое сообщение.
Вот пример использования static_assert для проверки размеров определенных типов данных:
1 2 3 4 5 6 7 |
static_assert(sizeof(long) == 8, "long must be 8 bytes"); static_assert(sizeof(int) == 4, "int must be 4 bytes"); int main() { return 0; } |
Результат на моем компьютере:
1>C:\ConsoleApplication1\main.cpp(19): error C2338: long must be 8 bytes
Поскольку static_assert обрабатывается компилятором, то условная часть static_assert также должна обрабатываться во время компиляции. Поскольку static_assert не обрабатывается во время выполнения программы, то стейтменты static_assert могут быть размещены в любом месте кода (даже в глобальном пространстве).
В C++11 диагностическое сообщение должно быть обязательно предоставлено в качестве второго параметра. В C++17 предоставление диагностического сообщения является необязательным.
В примере
found — это какая-то булева переменная? Или спец слово, используемое вместе с assert в каких-то не описанных здесь случаях?
булева переменная
Может быть булевой переменной. Или же каким-то объектом, значение которого можно неявно или явно преобразовать в
true
илиfalse
.