In the example in slide 2-assertions, assertions are used to check for the satisfaction of both the pre- and post-conditions of a function that computes the square root of its argument, employing a method known as Newton iteration.
double sqrt( double arg ) {\fbox{sqrt}
require ( arg >= 0 ); double r=arg, x=1, eps=0.0001; while( fabs(r - x) > eps ) { r=x; x=r-((r*r-arg)/(2*r)); } promise ( r - arg * arg <= eps ); return r; }
Whereas Eiffel directly supports the use of assertions by allowing access to the value of an instance variable before the execution of a method through the keyword old, the C++ programmer must rely on explicit programming to be able to compare the state before an operation with the state after the operation.
class counter {\fbox{counter}
public: counter(int n = 0) : _n(n) { require( n >= 0 ); promise( invariant() );\c{// check initial state}
} virtual void operator++() { require( true );\c{// empty pre-condition}
hold();\c{// save the previous state}
_n += 1; promise( _n == old_n + 1 && invariant() ); } int value() const { return _n; }\c{// no side effects}
virtual bool invariant() { return value() >= 0; } protected: int _n; int old_n; virtual void hold() { old_n = n; } };
Assertions may also be used to check whether the object is correctly initialized. The pre-condition stated in the constructor requires that the counter must start with a value not less than zero. In addition, the constructor checks whether the class invariant, stated in the (virtual) member function invariant, is satisfied. Similarly, after checking whether the post-condition of the function is true, the invariant is checked as well.
class bounded : public counter {\fbox{bounded}
public: bounded(int b = MAXINT) : counter(0), max(b) {} void operator++() { require( value() < max() );\c{// to prevent overflow}
counter::operator++(); } bool invariant() { return value() <= max && counter::invariant(); } private: int max; };
The class bounded, defined in slide 2-ass-2, refines the class counter by imposing an additional constraint that the value of the (bounded) counter must not exceed some user-defined maximum. This constraint is checked in the invariant function, together with the original , which was declared virtual to allow for overriding by inheritance.
In addition, the increment
From a formal perspective, the use of assertions may be regarded as a way of augmenting the type system supported by object-oriented languages. More importantly, from a software engineering perspective, the use of assertions provides a guideline for the design of classes and the use of inheritance. In the next chapter we will discuss the use of assertions and the notion of contracts in more detail.