有没有一种通用的方法来实现不变量?

Is there a common way to implement invariants?

这是一个基本示例,我想在其中添加不变变量,例如我的年龄不能小于 0。

#include "InvariantTest.h"
#include <iostream>
#include <string>
using namespace std;


int age;
string name;


void setAge(int a) {
    age = a;
}

void setName(string n) {
    name = n;
}

string getNameandAge() {
    string both;

    both = name + to_string(age);
    return both;

}

我找不到如何在 C++ 中实现不变量的规范。

来自标签描述:

In computer science, a predicate is called an invariant to a sequence of operations provided that: if the predicate is true before starting the sequence, then it is true at the end of the sequence.

谓词例如age > 0。一个操作序列例如

setAge(42);

另一个

setAge(-123);

为确保不违反不变量,您可以向 setAge 添加条件:

void setAge(int a) {
    if (a > 0) age = a;
}

您可以抛出异常、终止程序、采取任何其他操作,或者在分配值时静默忽略该值会违反不变量。没有“规范”,因为这取决于你想采取什么行动,以及调用者对传递无效参数的期望。

您也可以使 age 无符号,然后不变量 age >= 0 始终成立。

通常,除了 if (a > 0) age = a;

您也可以抛出一个完整的异常,而不是在合同未得到支持时断言,这样程序就会提前终止。您也可以在方法调用点捕获异常并优雅地处理错误。

存在其他错误处理技术,有些人更喜欢它们而不是异常,但这超出了问题的范围。

要保持​​不变量,您必须为所有可以修改受不变量影响的状态的函数建立 post 条件。在您的示例中,不变量影响全局变量。因此,post 条件会影响所有函数,因为所有函数都可能修改全局变量。这是一个问题。

对状态的访问应仅限于负责维护不变性的一小组函数。这通常称为封装。面向对象的解决方案是将状态存储在 class 的私有成员中,这限制了对维护不变性的成员函数的访问。

至于函数如何维持适用于它们的post条件,函数的实现者必须勤奋,并确保函数后的状态不违反不变性returns.

如果输入(即函数的参数)可能影响不变量,有许多不同的方法来维护不变量,各有优缺点:

  1. 简单地记录一个负数不能传递给setAge的预编码。如果调用者违反了该契约,则程序的行为可能由于随后违反不变量而未定义。

    这种方法可能是最快的,因为它不需要运行时检查。但它是最容易出错的,因为它将检查卸载给可能出错的调用者。

  2. 在这种特殊情况下,一个选项是使用无符号整数类型来表示年龄。由于它不能表示负值,它作为隐式文档,调用者没有办法违反合同。

    这种方法并不适用于所有类型,但在某些情况下可能非常有效和安全。

    但是在某些情况下,特别是无符号整数,这可能是危险的,因为程序员可能会做一些事情,比如减去两个年龄,数学结果会是负数,但由于无符号算术而变成很大的正值。在这种情况下,界面会很高兴,因为年龄满足前提条件,但行为可能与程序员希望的不同。

  3. 在运行时检查输入 - 例如使用 if 语句。如果输入错误,有几种处理错误的方法:

    • 抛出异常。
    • 设置一些默认值而不是给定错误值。这可能会有问题,因为状态与给定输入匹配的期望不成立。
    • 简单地终止进程。
    • 在文件中记录错误并可能直接联系开发人员(例如通过电子邮件),但继续使用其他方法之一,例如 1. 违反不变量,或 2. 设置默认值,终止过程等
  4. Select 在编译时使用上述哪种方法。这样做通常是为了在生产中使用更高检查但更慢的调试构建,以及更少检查、更快的构建。这就是标准 assert 宏的作用。

所有这些方法都很常见,正确的选择取决于您的需要,并且在某种程度上是主观的。