When an object checks its work before letting others see what happened. The promises made by a member function can be encoded as a test that is executed at the end of the member function. For example, if the member function List::removeFirst() promises that List::size() will return one less than it did before, an explicit test to this effect can be made at the end of the List::removeFirst() member function. The code associated with behavioral self-tests can be wrapped in an #ifdef so that it can be easily removed or reinstalled as desired: #include <stdexcept> #include <cassert> #include <cstdlib> #include <string> using namespace std; class List; class Node { private: friend List; int elem_; Node* next_; Node(int elem, Node* next) throw(); }; Node::Node(int elem, Node* next) throw() : elem_(elem) , next_(next) { } class Empty { }; class List { public: List() throw(); bool empty() const throw(); int size() const throw(); int peekAtFirst() const throw(Empty); void prepend(int x) throw(); int removeFirst() throw(Empty); // REQUIRE: empty() must return false // PROMISE: Return value will be the initial value of peekAtFirst() // PROMISE: size() will be reduced by 1 List (const List& list) throw(); // copy constructor List& operator= (const List& list) throw(); // assignment operator ~List() throw(); protected: Node* first_; }; List::List() throw() : first_(NULL) { } List::~List() throw() { while (first_ != NULL) removeFirst(); } bool List::empty() const throw() { return first_ == NULL; } int List::size() const throw() { int ans = 0; for (const Node* p = first_; p != NULL; p = p->next_) ++ans; return ans; } int List::peekAtFirst() const throw(Empty) { if (empty()) throw Empty("List::peekAtFirst()"); return first_->elem_; } void List::prepend(int x) throw() { first_ = new Node(x, first_); } int List::removeFirst() throw(Empty) { if (empty()) throw Empty("List::removeFirst()"); #ifndef NDEBUG int peekAtFirstInit = peekAtFirst(); int sizeInit = size(); #endif // remove first element from the List int result = first_->elem_; Node* oldFirstNode = first_; first_ = first_->next_; delete oldFirstNode; #ifndef NDEBUG assert(result == peekAtFirstInit); assert(size() == sizeInit - 1); #endif return result; } int main() { List a; a.prepend(42); a.prepend(24); int elem = a.removeFirst(); } Naturally the assert(...) statements can be replaced by other assertion-checking techniques, if desired. The point is that the object checks its own results. |