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

Is there a common way to implement invariants?

本文关键字:方法 实现 不变量 一种 有没有      更新时间:2023-10-16

这是一个我想添加intvariations的基本示例,例如我的年龄不能低于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++中实现不变量的规范。

来自标签描述:

在计算机科学中,谓词被称为操作序列的不变量,条件是:如果谓词在序列开始前为真,那么在序列结束时为真。

例如,谓词是age > 0。一系列操作是例如

setAge(42);

另一个

setAge(-123);

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

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

这取决于您抛出异常、终止程序、采取任何其他操作,或者在指定值时忽略该值,这将违反不变量。不存在";"范数";,因为这取决于您想要采取什么操作,以及调用方可以从传递无效参数中得到什么。

你也可以使age无符号,那么不变的age >= 0总是成立的。

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

对状态的访问应限于负责维护不变量的一小部分函数。这通常被称为封装。一个面向对象的解决方案是将状态存储在类的私有成员中,从而限制对维护不变量的成员函数的访问。

至于函数如何维护应用于它们的post条件,函数的实现者必须非常勤奋,并确保函数返回后状态不会违反不变量。

在输入(即函数的参数)可能影响不变量的情况下,有许多不同的方法来维护不变量,有各种优点和缺点:

  1. 只需记录一个负数可能不会传递给setAge的预编码。如果调用方违反了该约定,那么由于随后违反了不变量,程序的行为可能是未定义的。

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

  2. 在这种特殊情况下,可以选择使用无符号整数类型来表示年龄。由于它不能表示负值,所以它作为隐式文档工作,调用方没有办法违反约定。

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

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

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

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

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

通常,除了if (a > 0) age = a;之外,您还需要至少添加一个调试断言assert(a > 0)(这将在运行程序的调试构建时引发异常)

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

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