C++ 断言

在 C++ 中,断言是一种用于陈述或断定表达式必须为true的语句。

它用于检查除非存在 bug 否则不可能发生的情况。因此,它被用作调试工具,因为它会在断言变为false时终止程序。

例如,如果我们想计算一个非空的 vector nums 的平均值,那么断言

nums.size() > 0 

必须为真,除非程序中存在 bug。


创建 C++ 断言

在 C++ 中,我们可以使用 assert 预处理器宏来断言,该宏定义在 cassert 头文件中。

#include <cassert>

导入此文件后,我们可以使用以下语法创建断言

assert(expression);

在这里,如果expression求值为

  • 0(false)- assert 打印一条消息并终止程序
  • 1(true)- 不做任何操作,继续程序的正常执行

示例 1:C++ assert

#include <iostream>
#include <cassert>
using namespace std; int main() { int even_num = 3; // asserts the value of even_num must be even
assert((even_num % 2 == 0));
return 0; }

输出

Assertion `(even_num % 2 == 0)' failed.
Aborted

在上面的示例中,我们使用了 assert 宏来断言 even_num 的值应该是偶数。

assert(even_num % 2 == 0);

由于 even_num 的值为3,断言 even num % 2 == 0 失败。因此,程序终止并显示错误消息。

但是,如果我们把 even_num 改为2,程序就能无错误地执行。

注意: C++ assert 宏不提供自定义错误消息的参数。但是,我们可以使用逗号运算符添加错误描述。

assert(("The number should be even", even_num % 2  == 0));

这将产生错误消息

Assertion `("The number should be even", even_num % 2 == 0)' failed

禁用 C++ 中的 assert

断言用于检查除非存在 bug 否则不应发生的情况。因此,它们被用作调试工具。

因此,在应用程序发布之前,我们应该删除断言,因为已发布的构建应该能够正常运行,并且永远不应触发断言。

禁用断言的一种方法是在调试完成后在代码中搜索 assert 宏并手动删除它。

一个更好的方法是使用 NDEBUG 宏来禁用它。此宏会全局忽略所有断言。我们可以通过在包含 cassert 头文件之前使用 #define 指令来实现此宏。

因此,当我们完成调试并且不再需要断言时,可以添加以下代码。

#define NDEBUG
#include <cassert> 

注意: 对于具有多个文件的更大项目,NDEBUG 可以通过编译器命令行选项或 IDE 设置来设置。


静态断言

assert 宏用于运行时断言。相比之下,static_assert 用于编译时断言。

static_assert 的语法是

static_assert(const_boolean_expression, message);

这里,

  • const_boolean_expression - 编译时已知表达式
  • message - 如果断言失败时显示的消息

由于 static_assert 是一个关键字,我们不需要包含任何头文件。


示例 2:C++ Static Assert

#include <iostream>
using namespace std;

int main() {

static_assert(sizeof(int) >= 4, "Size of integer must be greater than or equal to 4 bytes");
return 0; }

在上面的示例中,我们使用了静态断言,以便在代码运行的平台上整数的大小至少为4字节。

由于数据类型的大小可能因平台而异,因此我们可以使用静态断言在编译时进行断言。

与在运行时进行的 assert 不同,如果 static_assert 失败,程序将无法编译。

在这里,如果在旧系统(16 位)上运行程序,断言将失败,因为 sizeof(int)2 字节


示例 3:C++ Static Assert - 泛型编程

#include <iostream>
using namespace std;

template <class T, int size>
class Container {

static_assert(size > 0, "The size of container cannot be less than 1");
T items[size]; }; int main() { Container<int, 0> st; return 0; }

输出

error: static assertion failed: The size of container cannot be less than 1

在上面的示例中,我们使用静态断言来确保泛型类中数组的大小大于0

在这里,我们创建了一个 Container 类的对象,使用

Container <int, 0> st;

由于 size 的值为0,编译会因为静态断言失败而中止。


何时使用断言

1. 不可达代码

这些是尝试运行程序时不会执行的代码。使用断言来确保不可达代码确实不可达。

让我们举个例子。

void unreachable_code_method() {
  cout << "Reachable code";
  return;

  // Unreachable code
  cout << "Unreachable code";
  assert(false);
}

让我们以一个没有 default 情况的 switch 语句为例。

switch (day_of_week) {
  case 1:
    cout << "It's Sunday!";
    break;
  case 2:
    cout << "It's Monday!";
    break;
  case 3:
    cout << "It's Tuesday!";
    break;
  case 4:
    cout << "It's Wednesday!";
    break;
  case 5:
    cout << "It's Thursday!";
    break;
  case 6:
    cout << "It's Friday!";
    break;
  case 7:
    cout << "It's Saturday!";
    break;

上面的 switch 语句表示一周的日期只能是1 到 7。没有 default 情况意味着程序员认为其中一种情况总是会被执行。

但是,可能还有一些尚未考虑到的情况,其中假设实际上是错误的。

应该使用断言来检查此假设,以确保不会达到 default switch 情况。

default:
  assert(("An Invalid Day", false));

如果 day_of_week 的值不是1 - 7 之间的任何值,则会发生断言错误。


2. 记录假设

为了记录其基本假设,许多程序员使用注释。让我们看一个例子。

if (i % 2 == 0) {
  ...
}
else { // We know (i % 2 == 1)
  ...
}

请改用断言。

随着程序的增长,注释可能会过时且不同步。但是,我们将被迫更新 assert 宏;否则,它们可能会因有效条件而失败。

if (i % 2 == 0) {
  ...
}
else {
  assert(i % 2 == 1);
  ...
}

何时不使用断言

1. 公共函数中的参数检查

公共函数中的参数可能由用户提供。

因此,如果使用断言来检查这些参数,条件可能会失败并导致断言错误。

不要使用断言,而是让其导致适当的运行时异常并处理这些异常。


2. 评估影响程序运行的表达式

不要调用方法或评估会导致副作用的异常。例如,

int n = 5;

// assertion with side effects
// here assertion is decrementing 'n' and asserts if n is even
assert(--n && n % 2 == 0);

在上面的示例中,语句 --n && n % 2 == 0 具有副作用,即它修改了变量 n 的值。此外,仅当启用断言时,assert 宏才会运行。

但是,如果我们使用 NDEBUG 宏禁用了断言,则上述递减语句永远不会被评估,因此如果 n 在代码的后续部分中被引用,则会影响整个代码。

因此,不要在 assert 宏中评估具有副作用的表达式,而是可以这样写

int n = 5;

//decrement n
--n;
  
// assertion without side effects
// here assertion asserts if n is even
assert(n % 2 == 0);

这确保了 n 的值在断言被启用或禁用时是相同的。

你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战