Читаем Программирование. Принципы и практика использования C++ Исправленное издание полностью

Написав предусловия (даже в виде комментариев), вы значительно повысите качество программы: это заставит вас задуматься о том, какие аргументы требует функция. Если вы не можете просто и ясно сформулировать эти требования в виде комментария, то, вероятно, вы плохо продумали свою программу. Опыт показывает, что такие предусловия и их проверки помогают избежать многих ошибок. Мы уже указывали, что ненавидим отладку; ясно сформулированные предусловия позволяют избежать конструктивных ошибок, а также устранить неправильное использование функций на ранних стадиях разработки программы. Вариант

int my_complicated_function(int a, int b, int c)

// Аргументы являются положительными и a < b < c

{

if (!(0

  error("Неверные аргументы функции mcf");

  // ...

}

сэкономит ваше время и силы по сравнению с более простым вариантом:

int my_complicated_function(int a, int b, int c)

{

  // ...

}

<p id="AutBody_Root084"><strong>5.10.1. Постусловия</strong></p>

Формулировка предусловий позволяет улучшить структуру программы и перехватить неправильное использование функций на ранних этапах программирования. Можно ли использовать эту идею где-нибудь еще? Да, на ум сразу приходит оператор return! Помимо всего прочего, следует указать, что именно функция будет возвращать; иначе говоря, если мы возвращаем из функции какое-то значение, то всегда обещаем вернуть что-то конкретное (а как иначе вызывающая функция будет знать, чего ей ждать?).

Вернемся к нашей функции, вычисляющей площадь прямоугольника (см. раздел 5.6.1).

// Вычисляет площадь прямоугольника;

// если аргументы неправильные, генерирует исключение Bad_area

int area(int length, int width)

{

  if (length<=0 || width <=0) throw Bad_area();

    return length*width;

}

Эта функция проверяет предусловия, но они не сформулированы в виде комментариев (для такой короткой функции это вполне допустимо), и считается, что все вычисления проводятся корректно (для таких простых вычислений это также вполне приемлемо). И тем не менее эту функцию можно было написать намного яснее.

int area(int length, int width)

// Вычисляет площадь прямоугольника;

// предусловия: аргументы length и width являются положительными

// постусловия: возвращает положительное значение, являющееся

// площадью

{

  if (length<=0 || width <=0) error("area() pre-condition");

  int a = length*width;

  if (a<=0) error("area() post-condition");

  return a;

}

Мы не можем проверить выполнение всех постусловий, но можем проверить их часть, убедившись, что возвращаемое число является положительным.

ПОПРОБУЙТЕ

Найдите пару значений, при которых предусловие выполняется, а постусловие — нет.

Пред- и постусловия обеспечивают проверку логичности кода. Они тесно связаны с понятиями инвариантов (раздел 9.4.3), корректности (разделы 4.2 и 5.2), а также с тестированием (глава 26).

<p id="AutBody_Root085"><strong>5.11. Тестирование</strong></p>

Как определить, когда следует остановить отладку? Ясно, что отладка должна идти до тех пор, пока не будут выявлены все ошибки, — или нам так покажется. А как узнать, что мы нашли последнюю ошибку? Мы не знаем. Последняя ошибка — это шутка программистов. Такой ошибки не существует. В большой программе никогда невозможно найти последнюю ошибку.

  Кроме отладки, нам необходим систематический подход к поиску ошибок. Он называется тестированием (testing) и рассматривается в разделе 7.3, упражнениях к главе 10 и в главе 26. В принципе тестирование — это выполнение программы с большим и систематически подобранным множеством входных данных и сравнение результатов с ожидаемыми. Выполнение программы с заданным множеством входных данных называют тестовым вариантом (test case). Для реальных программ могут потребоваться миллионы тестовых вариантов. Тестирование не может быть ручным, когда программист набирает варианты тест за тестом, поэтому в последующих главах мы рассмотрим инструменты, необходимые для правильного тестирования.

  Тем временем напомним, что тестирование основано на убеждении, что поиск ошибок выполняется правильно. Рассмотрим пример.

Точка зрения 1. Я умнее любой программы! Я могу взломать код @#$%^!

Точка зрения 2. Я вылизывал эту программу две недели. Она идеальна!

Перейти на страницу:

Похожие книги

Programming with POSIX® Threads
Programming with POSIX® Threads

With this practical book, you will attain a solid understanding of threads and will discover how to put this powerful mode of programming to work in real-world applications. The primary advantage of threaded programming is that it enables your applications to accomplish more than one task at the same time by using the number-crunching power of multiprocessor parallelism and by automatically exploiting I/O concurrency in your code, even on a single processor machine. The result: applications that are faster, more responsive to users, and often easier to maintain. Threaded programming is particularly well suited to network programming where it helps alleviate the bottleneck of slow network I/O. This book offers an in-depth description of the IEEE operating system interface standard, POSIX (Portable Operating System Interface) threads, commonly called Pthreads. Written for experienced C programmers, but assuming no previous knowledge of threads, the book explains basic concepts such as asynchronous programming, the lifecycle of a thread, and synchronization. You then move to more advanced topics such as attributes objects, thread-specific data, and realtime scheduling. An entire chapter is devoted to "real code," with a look at barriers, read/write locks, the work queue manager, and how to utilize existing libraries. In addition, the book tackles one of the thorniest problems faced by thread programmers-debugging-with valuable suggestions on how to avoid code errors and performance problems from the outset. Numerous annotated examples are used to illustrate real-world concepts. A Pthreads mini-reference and a look at future standardization are also included.

David Butenhof

Программирование, программы, базы данных