有没有一种通用的方法来实现不变量?
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.
如果输入(即函数的参数)可能影响不变量,有许多不同的方法来维护不变量,各有优缺点:
简单地记录一个负数不能传递给setAge
的预编码。如果调用者违反了该契约,则程序的行为可能由于随后违反不变量而未定义。
这种方法可能是最快的,因为它不需要运行时检查。但它是最容易出错的,因为它将检查卸载给可能出错的调用者。
在这种特殊情况下,一个选项是使用无符号整数类型来表示年龄。由于它不能表示负值,它作为隐式文档,调用者没有办法违反合同。
这种方法并不适用于所有类型,但在某些情况下可能非常有效和安全。
但是在某些情况下,特别是无符号整数,这可能是危险的,因为程序员可能会做一些事情,比如减去两个年龄,数学结果会是负数,但由于无符号算术而变成很大的正值。在这种情况下,界面会很高兴,因为年龄满足前提条件,但行为可能与程序员希望的不同。
在运行时检查输入 - 例如使用 if
语句。如果输入错误,有几种处理错误的方法:
- 抛出异常。
- 设置一些默认值而不是给定错误值。这可能会有问题,因为状态与给定输入匹配的期望不成立。
- 简单地终止进程。
- 在文件中记录错误并可能直接联系开发人员(例如通过电子邮件),但继续使用其他方法之一,例如 1. 违反不变量,或 2. 设置默认值,终止过程等
Select 在编译时使用上述哪种方法。这样做通常是为了在生产中使用更高检查但更慢的调试构建,以及更少检查、更快的构建。这就是标准 assert
宏的作用。
所有这些方法都很常见,正确的选择取决于您的需要,并且在某种程度上是主观的。
这是一个基本示例,我想在其中添加不变变量,例如我的年龄不能小于 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.
如果输入(即函数的参数)可能影响不变量,有许多不同的方法来维护不变量,各有优缺点:
简单地记录一个负数不能传递给
setAge
的预编码。如果调用者违反了该契约,则程序的行为可能由于随后违反不变量而未定义。这种方法可能是最快的,因为它不需要运行时检查。但它是最容易出错的,因为它将检查卸载给可能出错的调用者。
在这种特殊情况下,一个选项是使用无符号整数类型来表示年龄。由于它不能表示负值,它作为隐式文档,调用者没有办法违反合同。
这种方法并不适用于所有类型,但在某些情况下可能非常有效和安全。
但是在某些情况下,特别是无符号整数,这可能是危险的,因为程序员可能会做一些事情,比如减去两个年龄,数学结果会是负数,但由于无符号算术而变成很大的正值。在这种情况下,界面会很高兴,因为年龄满足前提条件,但行为可能与程序员希望的不同。
在运行时检查输入 - 例如使用
if
语句。如果输入错误,有几种处理错误的方法:- 抛出异常。
- 设置一些默认值而不是给定错误值。这可能会有问题,因为状态与给定输入匹配的期望不成立。
- 简单地终止进程。
- 在文件中记录错误并可能直接联系开发人员(例如通过电子邮件),但继续使用其他方法之一,例如 1. 违反不变量,或 2. 设置默认值,终止过程等
Select 在编译时使用上述哪种方法。这样做通常是为了在生产中使用更高检查但更慢的调试构建,以及更少检查、更快的构建。这就是标准
assert
宏的作用。
所有这些方法都很常见,正确的选择取决于您的需要,并且在某种程度上是主观的。