Reason(原因)
To detect misunderstandings about the result and possibly catch erroneous implementations.
一方面可以检查对于结果的错误理解,另一方面可以捕捉易错实现。
Example, bad(反面示例)
Consider:(考虑)
int area(int height, int width) { return height * width; } // bad
Here, we (incautiously) left out the precondition specification, so it is not explicit that height and width must be positive. We also left out the postcondition specification, so it is not obvious that the algorithm (height * width
) is wrong for areas larger than the largest integer. Overflow can happen. Consider using:
这里我们(鲁莽地)遗漏了定义前置条件,因此没有明确高度和宽度必须为正值这件事。我们也没有定义后置条件,因此当面积大于最大整数的时候算法存在错误这件事也不容易被察觉。这里会发生溢出错误。考虑使用:
int area(int height, int width)
{
auto res = height * width;
Ensures(res > 0);
return res;
}
译者注:和Expects表示前置条件相对,Ensures用于表现后置条件。这符合现代C++的发展思路:提倡表达目的而不是做法。
Example, bad(反面示例)
Consider a famous security bug:(考虑一个著名的安全性bug)
void f() // problematic
{
char buffer[MAX];
// ...
memset(buffer, 0, sizeof(buffer));
}
There was no postcondition stating that the buffer should be cleared and the optimizer eliminated the apparently redundant memset()
call:
不存在后置条件表明buffer应该被清空,因此优化程序会消除明显多余的memset调用。
void f() // better
{
char buffer[MAX];
// ...
memset(buffer, 0, sizeof(buffer));
Ensures(buffer[0] == 0);
}
译者注:这个例子比上一个稍好,但是Ensures的内容更像是专门防止优化的,似乎并没有反映本来的想法。
Note(注意)
Postconditions are often informally stated in a comment that states the purpose of a function; Ensures()
can be used to make this more systematic, visible, and checkable.
后置条件经常在描述函数目的的注释中被非正式表达。使用Ensures()可以让后置条件更系统,直观和便于检查。
Note(注意)
Postconditions are especially important when they relate to something that is not directly reflected in a returned result, such as a state of a data structure used.
当后置条件和某些不会直接反映到返回值的结果时显得尤为重要,例如数据结构被使用过的状态等。
译者注:调用者的可能更加注意有没有,而忽略状态等细节。
Example(示例)
Consider a function that manipulates a Record
, using a mutex
to avoid race conditions:
考虑一个操作记录的函数,它使用mutex防止竞争条件。
mutex m;
void manipulate(Record& r) // don't
{
m.lock();
// ... no m.unlock() ...
}
Here, we "forgot" to state that the mutex
should be released, so we don't know if the failure to ensure release of the mutex
was a bug or a feature. Stating the postcondition would have made it clear:
这里我们“忘记了”说明mutex应该被释放这件事,因此我们不知道保证释放mutex失败这件事是bug还是功能。说明后置条件可以明确这件事:
void manipulate(Record& r) // postcondition: m is unlocked upon exit
{
m.lock();
// ... no m.unlock() ...
}
The bug is now obvious (but only to a human reading comments).
Better still, use RAII to ensure that the postcondition ("the lock must be released") is enforced in code:
有bug这件事已经很明显了(但还只是人类可读的注释)。更好一些的选择是:使用RAII来保证后置条件(“锁必须被释放”)会被强制释放。
void manipulate(Record& r) // best
{
lock_guard
_ {m}; // ...
}
译者注:lock_guard是C++11开始的新特性,其实就是简单的RAII封装,在构造函数中进行加锁,析构函数中进行解锁。这样做的好处是保证函数退出时,锁一定被释放。
Note(注意)
Ideally, postconditions are stated in the interface/declaration so that users can easily see them. Only postconditions related to the users can be stated in the interface. Postconditions related only to internal state belongs in the definition/implementation.
理想情况下,后置条件在接口或声明时说明,这样用户就可以简单地看到它们。只有和用户相关的后置条件才可以在接口中描述。只和内部状态有关的后置条件属于定义和实现。
Enforcement(实施建议)
(Not enforceable) This is a philosophical guideline that is infeasible to check directly in the general case. Domain specific checkers (like lock-holding checkers) exist for many toolchains.
(非强制)这是一条哲学层面的准则,通常没有办法直接检查。在很多工具链中会存在领域限定的检查器(例如锁保持检查器)。
觉得本文有帮助,欢迎点赞并分享给更多的朋友!
阅读更多更新文章,请关注微信公众号【面向对象思考】